diff --git a/system_modules/naprender/src/bufferbinding.h b/system_modules/naprender/src/bufferbinding.h index 2eb54972e..1e5598873 100644 --- a/system_modules/naprender/src/bufferbinding.h +++ b/system_modules/naprender/src/bufferbinding.h @@ -87,6 +87,11 @@ namespace nap */ virtual size_t getSize() const { assert(mBuffer != nullptr); return mBuffer->getSize(); } + /** + * @return The element size in bytes + */ + virtual size_t getElementSize() const { assert(mBuffer != nullptr); return mBuffer->getElementSize(); } + /** * @return the base GPU buffer, nullptr if not set */ diff --git a/system_modules/naprender/src/bufferbindinginstance.cpp b/system_modules/naprender/src/bufferbindinginstance.cpp index b6a36b7b4..50711d4ed 100644 --- a/system_modules/naprender/src/bufferbindinginstance.cpp +++ b/system_modules/naprender/src/bufferbindinginstance.cpp @@ -72,12 +72,32 @@ namespace nap static const ShaderVariableDeclaration& getBufferDeclaration(const BufferObjectDeclaration& declaration) { // If a buffer object declaration is passed, we can safely acquire the actual buffer declaration from it - if (declaration.get_type() == RTTI_OF(BufferObjectDeclaration)) + return declaration.getBufferDeclaration(); + } + + + /** + * Checks whether a buffer delcaration is compatible with a buffer binding. + * It does so by ensuring the shader variable stride is equal to the buffer binding element size. + */ + static bool isCompatible(const ShaderVariableDeclaration& declaration, const BufferBinding& binding) + { + assert(binding.getBuffer() != nullptr); + + // If the declaration is a struct buffer or value array, check its stride + auto declaration_type = declaration.get_type(); + if (declaration_type == RTTI_OF(ShaderVariableStructBufferDeclaration)) { - const BufferObjectDeclaration* buffer_object_declaration = rtti_cast(&declaration); - return buffer_object_declaration->getBufferDeclaration(); + const auto& struct_buffer_declaration = static_cast(declaration); + return struct_buffer_declaration.mStride == binding.getBuffer()->getElementSize(); } - return declaration; + else if (declaration_type == RTTI_OF(ShaderVariableValueArrayDeclaration)) + { + const auto &struct_buffer_declaration = static_cast(declaration); + return struct_buffer_declaration.mStride == binding.getBuffer()->getElementSize(); + } + // The declaration is for a primitive value, so we check its size directly + return declaration.mSize == binding.getBuffer()->getElementSize(); } @@ -92,35 +112,28 @@ namespace nap // Ensure we retrieve the actual buffer declaration const auto& buffer_declaration = getBufferDeclaration(declaration); - rtti::TypeInfo declaration_type = buffer_declaration.get_type(); // If the buffer binding was created from a resource, ensure its element count matches // the one in the shader declaration and ensure the descriptortype is storage if (binding != nullptr) { - // Verify descriptortype + // Verify descriptor type if (!errorState.check((binding->getBuffer()->getBufferUsageFlags() & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) > 0, "DescriptorType mismatch. StructGPUBuffer 'DescriptorType' property must be 'Storage' to be used as a buffer binding")) return nullptr; // Verify bounds - if (!errorState.check(binding->getSize() == buffer_declaration.mSize, - utility::stringFormat("Mismatch between total buffer size in shader and json for buffer binding '%s'. Please refer to the alignment requirements for shader resources in Section 15.6.4 of the Vulkan specification", buffer_declaration.mName.c_str()))) + if (!errorState.check(isCompatible(buffer_declaration, *binding), + utility::stringFormat("Mismatch between element stride and buffer element size for buffer binding '%s'. Please refer to the alignment requirements for shader resources in Section 15.6.4 of the Vulkan specification", buffer_declaration.mName.c_str()))) return nullptr; } + auto declaration_type = buffer_declaration.get_type(); + // Creates a BufferBindingStructInstance if (declaration_type == RTTI_OF(ShaderVariableStructBufferDeclaration)) { const auto& struct_buffer_declaration = static_cast(buffer_declaration); - if (binding != nullptr) - { - // Verify count - if (!errorState.check(binding->getCount() == struct_buffer_declaration.mNumElements, - "Encountered mismatch in array elements between array in material and array in shader")) - return nullptr; - } - return createBufferBindingInstance( binding_name, struct_buffer_declaration, binding, bufferChangedCallback, errorState); } @@ -128,15 +141,7 @@ namespace nap // Creates a BufferBindingNumericInstance if (declaration_type == RTTI_OF(ShaderVariableValueArrayDeclaration)) { - const ShaderVariableValueArrayDeclaration* value_array_declaration = rtti_cast(&buffer_declaration); - if (binding != nullptr) - { - // Verify count - if (!errorState.check(binding->getCount() == value_array_declaration->mNumElements, - "Encountered mismatch in array elements between array in material and array in shader")) - return nullptr; - } - + const auto* value_array_declaration = rtti_cast(&buffer_declaration); if (value_array_declaration->mElementType == EShaderVariableValueType::UInt) { return createBufferBindingInstance( @@ -203,7 +208,7 @@ namespace nap void BufferBindingStructInstance::setBuffer(StructBuffer& buffer) { - assert(buffer.getSize() == mDeclaration->mSize); + NAP_ASSERT_MSG(mDeclaration->mStride == buffer.getElementSize(), "Buffer declaration stride is not equal to buffer element size"); BufferBindingInstance::mBuffer = &buffer; raiseChanged(); } @@ -212,7 +217,7 @@ namespace nap void BufferBindingStructInstance::setBuffer(const BufferBindingStruct& resource) { assert(resource.mBuffer != nullptr); - assert(resource.mBuffer->getSize() == mDeclaration->mSize); + NAP_ASSERT_MSG(mDeclaration->mStride == resource.mBuffer->getElementSize(), "Buffer declaration stride is not equal to buffer element size"); BufferBindingInstance::mBuffer = resource.mBuffer.get(); raiseChanged(); } diff --git a/system_modules/naprender/src/bufferbindinginstance.h b/system_modules/naprender/src/bufferbindinginstance.h index 75cb7622e..578329b24 100644 --- a/system_modules/naprender/src/bufferbindinginstance.h +++ b/system_modules/naprender/src/bufferbindinginstance.h @@ -340,7 +340,7 @@ namespace nap template void TypedBufferBindingNumericInstance::setBuffer(TypedGPUBufferNumeric& buffer) { - assert(buffer.getSize() == mDeclaration->mSize); + NAP_ASSERT_MSG(mDeclaration->mStride == buffer.getElementSize(), "Buffer declaration stride is not equal to buffer element size"); BufferBindingInstance::mBuffer = &buffer; raiseChanged(); } @@ -348,7 +348,7 @@ namespace nap template void TypedBufferBindingNumericInstance::setBuffer(const TypedBufferBindingNumeric& resource) { - assert(resource.mBuffer.getSize() == mDeclaration->mSize); + NAP_ASSERT_MSG(mDeclaration->mStride == resource.mBuffer.getElementSize(), "Buffer declaration stride is not equal to buffer element size"); BufferBindingInstance::mBuffer = resource.mBuffer.get(); raiseChanged(); } diff --git a/system_modules/naprender/src/depthrendertarget.cpp b/system_modules/naprender/src/depthrendertarget.cpp index 706f767d8..19c74691d 100644 --- a/system_modules/naprender/src/depthrendertarget.cpp +++ b/system_modules/naprender/src/depthrendertarget.cpp @@ -66,7 +66,7 @@ namespace nap return false; // Bind textures as attachments - VkImageView attachment = mDepthTexture->getHandle().getView(); + const auto attachment = std::as_const(*mDepthTexture).getHandle().getView(); // Create framebuffer VkFramebufferCreateInfo framebufferInfo = {}; @@ -128,6 +128,9 @@ namespace nap void DepthRenderTarget::endRendering() { vkCmdEndRenderPass(mRenderService->getCurrentCommandBuffer()); + + // Sync image data with render pass final layout + mDepthTexture->syncLayout(); } diff --git a/system_modules/naprender/src/rendertarget.cpp b/system_modules/naprender/src/rendertarget.cpp index 7aae82a5b..db52b6400 100644 --- a/system_modules/naprender/src/rendertarget.cpp +++ b/system_modules/naprender/src/rendertarget.cpp @@ -5,6 +5,7 @@ // Local Includes #include "rendertarget.h" #include "renderservice.h" +#include "textureutils.h" // External Includes #include @@ -18,6 +19,7 @@ RTTI_BEGIN_CLASS_NO_DEFAULT_CONSTRUCTOR(nap::RenderTarget, "Color and Depth text RTTI_PROPERTY("SampleShading", &nap::RenderTarget::mSampleShading, nap::rtti::EPropertyMetaData::Default, "Reduces texture aliasing at higher computational cost") RTTI_PROPERTY("Samples", &nap::RenderTarget::mRequestedSamples, nap::rtti::EPropertyMetaData::Default, "Number of MSAA samples to use") RTTI_PROPERTY("ClearColor", &nap::RenderTarget::mClearColor, nap::rtti::EPropertyMetaData::Default, "Initial clear value") + RTTI_PROPERTY("Clear", &nap::RenderTarget::mClear, nap::rtti::EPropertyMetaData::Default, "Whether to clear the render target") RTTI_END_CLASS namespace nap @@ -87,11 +89,12 @@ namespace nap mSampleShading = false; } + // Store whether a depth texture resource is set mHasDepthTexture = mDepthTexture != nullptr; // Set framebuffer size - glm::ivec2 size = mColorTexture->getSize(); + const auto size = mColorTexture->getSize(); VkExtent2D framebuffer_size = { (uint32)size.x, (uint32)size.y }; // Store as attachments @@ -105,12 +108,12 @@ namespace nap if (!errorState.check(mRasterizationSamples == VK_SAMPLE_COUNT_1_BIT, "Depth resolve attachments are not supported. Set the sample count to one if a depth texture resource is desired.")) return false; - if (!createRenderPass(mRenderService->getDevice(), mColorTexture->getFormat(), mDepthTexture->getFormat(), mRasterizationSamples, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, true, mRenderPass, errorState)) + if (!createRenderPass(mRenderService->getDevice(), mColorTexture->getFormat(), mDepthTexture->getFormat(), mRasterizationSamples, mColorTexture->getTargetLayout(), mClear, true, mRenderPass, errorState)) return false; } else { - if (!createRenderPass(mRenderService->getDevice(), mColorTexture->getFormat(), mRenderService->getDepthFormat(), mRasterizationSamples, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, mRenderPass, errorState)) + if (!createRenderPass(mRenderService->getDevice(), mColorTexture->getFormat(), mRenderService->getDepthFormat(), mRasterizationSamples, mColorTexture->getTargetLayout(), mClear, false, mRenderPass, errorState)) return false; } @@ -119,8 +122,8 @@ namespace nap // Bind textures as attachments if (hasDepthTexture()) { - attachments[0] = mColorTexture->getHandle().getView(); - attachments[1] = mDepthTexture->getHandle().getView(); + attachments[0] = std::as_const(*mColorTexture).getHandle().getView(); + attachments[1] = std::as_const(*mDepthTexture).getHandle().getView(); attachments[2] = VK_NULL_HANDLE; } else @@ -129,7 +132,7 @@ namespace nap if (!createDepthResource(*mRenderService, framebuffer_size, mRasterizationSamples, mDepthImage, errorState)) return false; - attachments[0] = mColorTexture->getHandle().getView(); + attachments[0] = std::as_const(*mColorTexture).getHandle().getView(); attachments[1] = mDepthImage.getView(); attachments[2] = VK_NULL_HANDLE; } @@ -147,7 +150,7 @@ namespace nap // Bind textures as attachments attachments[0] = mColorImage.getView(); attachments[1] = mDepthImage.getView(); - attachments[2] = mColorTexture->getHandle().getView(); + attachments[2] = std::as_const(*mColorTexture).getHandle().getView(); } // Create framebuffer @@ -168,10 +171,47 @@ namespace nap void RenderTarget::beginRendering() { - glm::ivec2 size = mColorTexture->getSize(); - std::array clearValues = {}; - clearValues[0].color = { mClearColor[0], mClearColor[1], mClearColor[2], mClearColor[3] }; - clearValues[1].depthStencil = { 1.0f, 0 }; + // Transition target texture image layout to color attachment optimal only when clear is disabled + // We would otherwise violate the Vulkan spec when using LOAD_OP_LOAD in the render pass + if (!mClear) + { + if (mRasterizationSamples == VK_SAMPLE_COUNT_1_BIT) + { + // When MSAA is disabled, transition the color texture layout + // This operation requires a non-const image data handle + utility::transitionImageLayout(mRenderService->getCurrentCommandBuffer(), + mColorTexture->getHandle(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + 0, mColorTexture->getMipLevels(), + VK_IMAGE_ASPECT_COLOR_BIT); + } + else + { + // When MSAA is enabled, transition the color image + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = mColorImage.getImage(); + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + vkCmdPipelineBarrier(mRenderService->getCurrentCommandBuffer(), VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + } + } + + const auto size = mColorTexture->getSize(); + std::array clear_values = {}; + clear_values[0].color = { mClearColor[0], mClearColor[1], mClearColor[2], mClearColor[3] }; + clear_values[1].depthStencil = { 1.0f, 0 }; + clear_values[2].color = { mClearColor[0], mClearColor[1], mClearColor[2], mClearColor[3] }; // Setup render pass VkRenderPassBeginInfo renderPassInfo = {}; @@ -180,8 +220,8 @@ namespace nap renderPassInfo.framebuffer = mFramebuffer; renderPassInfo.renderArea.offset = { 0, 0 }; renderPassInfo.renderArea.extent = { static_cast(size.x), static_cast(size.y) };; - renderPassInfo.clearValueCount = static_cast(clearValues.size()); - renderPassInfo.pClearValues = clearValues.data(); + renderPassInfo.clearValueCount = static_cast(clear_values.size()); + renderPassInfo.pClearValues = clear_values.data(); // Begin render pass vkCmdBeginRenderPass(mRenderService->getCurrentCommandBuffer(), &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); @@ -208,6 +248,9 @@ namespace nap void RenderTarget::endRendering() { vkCmdEndRenderPass(mRenderService->getCurrentCommandBuffer()); + + // Sync image data with render pass final layout + mColorTexture->syncLayout(); } diff --git a/system_modules/naprender/src/rendertarget.h b/system_modules/naprender/src/rendertarget.h index a38a44cc0..48141443c 100644 --- a/system_modules/naprender/src/rendertarget.h +++ b/system_modules/naprender/src/rendertarget.h @@ -180,6 +180,7 @@ namespace nap ERasterizationSamples mRequestedSamples = ERasterizationSamples::One; ///< Property: 'Samples' The number of samples used during Rasterization. For better results turn on 'SampleShading'. ResourcePtr mColorTexture; ///< Property: 'ColorTexture' texture to render to ResourcePtr mDepthTexture; ///< Property: 'DepthTexture' optional depth texture to render to + bool mClear = true; ///< Property: 'Clear' whether to clear the render target private: RenderService* mRenderService; diff --git a/system_modules/naprender/src/rendertexture2d.cpp b/system_modules/naprender/src/rendertexture2d.cpp index 983d2f9ad..bf44d6688 100644 --- a/system_modules/naprender/src/rendertexture2d.cpp +++ b/system_modules/naprender/src/rendertexture2d.cpp @@ -119,6 +119,12 @@ namespace nap } + void RenderTexture2D::syncLayout() + { + getHandle().mCurrentLayout = getTargetLayout(); + } + + ////////////////////////////////////////////////////////////////////////// // DepthRenderTexture2D ////////////////////////////////////////////////////////////////////////// @@ -161,4 +167,9 @@ namespace nap } } } + + void DepthRenderTexture2D::syncLayout() + { + getHandle().mCurrentLayout = getTargetLayout(); + } } diff --git a/system_modules/naprender/src/rendertexture2d.h b/system_modules/naprender/src/rendertexture2d.h index 7b89d5911..07117c878 100644 --- a/system_modules/naprender/src/rendertexture2d.h +++ b/system_modules/naprender/src/rendertexture2d.h @@ -23,7 +23,9 @@ namespace nap */ class NAPAPI RenderTexture2D : public Texture2D { - RTTI_ENABLE(Texture2D) + friend class RenderTarget; + + RTTI_ENABLE(Texture2D) public: /** * All supported render texture 2D formats. @@ -48,10 +50,10 @@ namespace nap */ virtual bool init(utility::ErrorState& errorState) override; - /** - * @return Vulkan GPU data handle, including image and view. - */ - virtual const ImageData& getHandle() const override { return mImageData; } + /** + * Updates image layout to the target layout after a render pass. + */ + void syncLayout(); int mWidth = 0; ///< Property: 'Width' width of the texture in texels int mHeight = 0; ///< Property: 'Height' of the texture in texels @@ -68,7 +70,6 @@ namespace nap */ class NAPAPI DepthRenderTexture2D : public Texture2D { - friend class DepthRenderTarget; RTTI_ENABLE(Texture2D) public: /** @@ -89,10 +90,10 @@ namespace nap */ virtual bool init(utility::ErrorState& errorState) override; - /** - * @return Vulkan GPU data handle, including image and view. - */ - virtual const ImageData& getHandle() const override { return mImageData; } + /** + * Updates image layout to the target layout after a render pass. + */ + void syncLayout(); int mWidth = 0; ///< Property: 'Width' width of the texture in texels int mHeight = 0; ///< Property: 'Height' of the texture in texels diff --git a/system_modules/naprender/src/rendertexturecube.cpp b/system_modules/naprender/src/rendertexturecube.cpp index 37c900a2f..3acd680fa 100644 --- a/system_modules/naprender/src/rendertexturecube.cpp +++ b/system_modules/naprender/src/rendertexturecube.cpp @@ -119,6 +119,12 @@ namespace nap } + void RenderTextureCube::syncLayout() + { + getHandle().mCurrentLayout = getTargetLayout(); + } + + ////////////////////////////////////////////////////////////////////////// // DepthRenderTextureCube ////////////////////////////////////////////////////////////////////////// diff --git a/system_modules/naprender/src/rendertexturecube.h b/system_modules/naprender/src/rendertexturecube.h index 73e342da1..b128322a0 100644 --- a/system_modules/naprender/src/rendertexturecube.h +++ b/system_modules/naprender/src/rendertexturecube.h @@ -60,6 +60,11 @@ namespace nap */ virtual bool init(utility::ErrorState& errorState) override; + /** + * Updates image layout to the target layout after a render pass. + */ + void syncLayout(); + int mWidth = 0; ///< Property: 'Width' width of a cube face texture in texels int mHeight = 0; ///< Property: 'Height' of a cube face texture in texels EColorSpace mColorSpace = EColorSpace::Linear; ///< Property: 'ColorSpace' texture color space diff --git a/system_modules/naprender/src/renderutils.cpp b/system_modules/naprender/src/renderutils.cpp index 924d43aad..6db838cb8 100644 --- a/system_modules/naprender/src/renderutils.cpp +++ b/system_modules/naprender/src/renderutils.cpp @@ -22,113 +22,125 @@ namespace nap { namespace utility { - bool createRenderPass(VkDevice device, VkFormat colorFormat, VkFormat depthFormat, VkSampleCountFlagBits samples, VkImageLayout targetLayout, bool consumeDepth, VkRenderPass& renderPass, utility::ErrorState& errorState) - { - if (!errorState.check(targetLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR || targetLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, "Failed to create render pass. Unsupported target layout.")) - return false; - - bool multi_sample = samples != VK_SAMPLE_COUNT_1_BIT; + bool createRenderPass(VkDevice device, VkFormat colorFormat, VkFormat depthFormat, VkSampleCountFlagBits samples, VkImageLayout targetLayout, bool clear, bool consumeDepth, VkRenderPass& renderPass, utility::ErrorState& errorState) + { + if (!errorState.check(targetLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR || targetLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, "Failed to create render pass. Unsupported target layout.")) + return false; - VkAttachmentDescription color_attachment = {}; - color_attachment.format = colorFormat; - color_attachment.samples = samples; - color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - color_attachment.storeOp = multi_sample ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE; - color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - color_attachment.finalLayout = !multi_sample ? targetLayout : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + bool multi_sample = samples != VK_SAMPLE_COUNT_1_BIT; - VkAttachmentDescription depth_attachment = {}; - depth_attachment.format = depthFormat; - depth_attachment.samples = samples; - depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - depth_attachment.storeOp = consumeDepth ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE; - depth_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - depth_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - depth_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - depth_attachment.finalLayout = consumeDepth ? targetLayout : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + VkAttachmentDescription color_attachment = {}; + color_attachment.format = colorFormat; + color_attachment.samples = samples; + color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + color_attachment.loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; + color_attachment.initialLayout = clear ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkAttachmentReference color_attachment_ref = {}; - color_attachment_ref.attachment = 0; - color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + if (!multi_sample) + { + color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + color_attachment.finalLayout = targetLayout; + } + else + { + color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + color_attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } - VkAttachmentReference depth_attachment_ref = {}; - depth_attachment_ref.attachment = 1; - depth_attachment_ref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + VkAttachmentDescription depth_attachment = {}; + depth_attachment.format = depthFormat; + depth_attachment.samples = samples; + depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + depth_attachment.storeOp = consumeDepth ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE; + depth_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + depth_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depth_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + depth_attachment.finalLayout = consumeDepth ? targetLayout : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkAttachmentReference color_attachment_ref = {}; + color_attachment_ref.attachment = 0; + color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference depth_attachment_ref = {}; + depth_attachment_ref.attachment = 1; + depth_attachment_ref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_attachment_ref; + subpass.pDepthStencilAttachment = &depth_attachment_ref; + + std::array dependencies; + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | (consumeDepth ? VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT : 0); + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo renderpass_info = {}; + renderpass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderpass_info.subpassCount = 1; + renderpass_info.pSubpasses = &subpass; + renderpass_info.dependencyCount = static_cast(dependencies.size()); + renderpass_info.pDependencies = dependencies.data(); + + // Single-sample render pass + if (!multi_sample) + { + std::array attachments = { color_attachment, depth_attachment }; + renderpass_info.attachmentCount = static_cast(attachments.size()); + renderpass_info.pAttachments = attachments.data(); + + return errorState.check(vkCreateRenderPass(device, &renderpass_info, nullptr, &renderPass) == VK_SUCCESS, "Failed to create render pass"); + } - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_attachment_ref; - subpass.pDepthStencilAttachment = &depth_attachment_ref; + // Multi-sample render pass + VkAttachmentDescription resolve_attachment = {}; + resolve_attachment.format = colorFormat; + resolve_attachment.samples = VK_SAMPLE_COUNT_1_BIT; + resolve_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + resolve_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + resolve_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + resolve_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + resolve_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + resolve_attachment.finalLayout = targetLayout; - std::array dependencies; - dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; - dependencies[0].dstSubpass = 0; - dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; - dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + VkAttachmentReference resolve_attachment_ref = {}; + resolve_attachment_ref.attachment = 2; + resolve_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - dependencies[1].srcSubpass = 0; - dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; - dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | (consumeDepth ? VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT : 0); - dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + subpass.pResolveAttachments = &resolve_attachment_ref; - VkRenderPassCreateInfo renderpass_info = {}; - renderpass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderpass_info.subpassCount = 1; - renderpass_info.pSubpasses = &subpass; - renderpass_info.dependencyCount = static_cast(dependencies.size()); - renderpass_info.pDependencies = dependencies.data(); + std::array attachments = { color_attachment, depth_attachment, resolve_attachment }; + renderpass_info.attachmentCount = static_cast(attachments.size()); + renderpass_info.pAttachments = attachments.data(); - // Single-sample render pass - if (!multi_sample) - { - std::array attachments = { color_attachment, depth_attachment }; - renderpass_info.attachmentCount = static_cast(attachments.size()); - renderpass_info.pAttachments = attachments.data(); + return errorState.check(vkCreateRenderPass(device, &renderpass_info, nullptr, &renderPass) == VK_SUCCESS, "Failed to create multi-sample render pass"); + } - return errorState.check(vkCreateRenderPass(device, &renderpass_info, nullptr, &renderPass) == VK_SUCCESS, "Failed to create render pass"); - } - // Multi-sample render pass - else - { - VkAttachmentDescription resolve_attachment{}; - resolve_attachment.format = colorFormat; - resolve_attachment.samples = VK_SAMPLE_COUNT_1_BIT; - resolve_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - resolve_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - resolve_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - resolve_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - resolve_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - resolve_attachment.finalLayout = targetLayout; - - VkAttachmentReference resolve_attachment_ref{}; - resolve_attachment_ref.attachment = 2; - resolve_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - subpass.pResolveAttachments = &resolve_attachment_ref; - - std::array attachments = { color_attachment, depth_attachment, resolve_attachment }; - renderpass_info.attachmentCount = static_cast(attachments.size()); - renderpass_info.pAttachments = attachments.data(); - - return errorState.check(vkCreateRenderPass(device, &renderpass_info, nullptr, &renderPass) == VK_SUCCESS, "Failed to create multi-sample render pass"); - } - } + bool createRenderPass(VkDevice device, VkFormat colorFormat, VkFormat depthFormat, VkSampleCountFlagBits samples, VkImageLayout targetLayout, bool consumeDepth, VkRenderPass& renderPass, utility::ErrorState& errorState) + { + return createRenderPass(device, colorFormat, depthFormat, samples, targetLayout, true, consumeDepth, renderPass, errorState); + } - bool createRenderPass(VkDevice device, VkFormat colorFormat, VkFormat depthFormat, VkSampleCountFlagBits samples, VkImageLayout targetLayout, VkRenderPass& renderPass, utility::ErrorState& errorState) - { - return createRenderPass(device, colorFormat, depthFormat, samples, targetLayout, false, renderPass, errorState); - } + bool createRenderPass(VkDevice device, VkFormat colorFormat, VkFormat depthFormat, VkSampleCountFlagBits samples, VkImageLayout targetLayout, VkRenderPass& renderPass, utility::ErrorState& errorState) + { + return createRenderPass(device, colorFormat, depthFormat, samples, targetLayout, true, renderPass, errorState); + } bool createDepthOnlyRenderPass(VkDevice device, VkFormat depthFormat, VkRenderPass& renderPass, utility::ErrorState& errorState) diff --git a/system_modules/naprender/src/renderutils.h b/system_modules/naprender/src/renderutils.h index 4dc81469c..66890a152 100644 --- a/system_modules/naprender/src/renderutils.h +++ b/system_modules/naprender/src/renderutils.h @@ -35,7 +35,12 @@ namespace nap namespace utility { - /** + /** + * Creates a single or multi-sample renderpass based on rasterization samples and color/depth formats. + */ + bool NAPAPI createRenderPass(VkDevice device, VkFormat colorFormat, VkFormat depthFormat, VkSampleCountFlagBits samples, VkImageLayout targetLayout, bool clear, bool consumeDepth, VkRenderPass& renderPass, utility::ErrorState& errorState); + + /** * Creates a single or multi-sample renderpass based on rasterization samples and color/depth formats. */ bool NAPAPI createRenderPass(VkDevice device, VkFormat colorFormat, VkFormat depthFormat, VkSampleCountFlagBits samples, VkImageLayout targetLayout, bool consumeDepth, VkRenderPass& renderPass, utility::ErrorState& errorState); diff --git a/system_modules/naprender/src/renderwindow.cpp b/system_modules/naprender/src/renderwindow.cpp index e04605dd6..be207a88d 100644 --- a/system_modules/naprender/src/renderwindow.cpp +++ b/system_modules/naprender/src/renderwindow.cpp @@ -967,9 +967,10 @@ namespace nap render_pass_info.renderArea.extent = mSwapchainExtent; // Clear color - std::array clear_values = {}; + std::array clear_values = {}; clear_values[0].color = { mClearColor[0], mClearColor[1], mClearColor[2], mClearColor[3] }; clear_values[1].depthStencil = { 1.0f, 0 }; + clear_values[2].color = { mClearColor[0], mClearColor[1], mClearColor[2], mClearColor[3] }; render_pass_info.clearValueCount = static_cast(clear_values.size()); render_pass_info.pClearValues = clear_values.data(); diff --git a/system_modules/naprender/src/shader.cpp b/system_modules/naprender/src/shader.cpp index 7810c9e06..c4c0fea9a 100644 --- a/system_modules/naprender/src/shader.cpp +++ b/system_modules/naprender/src/shader.cpp @@ -575,10 +575,10 @@ static bool addShaderVariablesRecursive(nap::ShaderVariableStructDeclaration& pa for (int index = 0; index < type.member_types.size(); ++index) { spirv_cross::SPIRType member_type = compiler.get_type(type.member_types[index]); + auto member_size = compiler.get_declared_struct_member_size(type, index); - std::string name = compiler.get_member_name(type.self, index); + auto name = compiler.get_member_name(type.self, index); int absoluteOffset = parentOffset + compiler.type_struct_member_offset(type, index); - size_t member_size = compiler.get_declared_struct_member_size(type, index); std::string full_path = path + "." + name; @@ -588,12 +588,11 @@ static bool addShaderVariablesRecursive(nap::ShaderVariableStructDeclaration& pa bool is_array = !member_type.array.empty(); if (is_array) { - int num_elements = member_type.array[0]; - + auto num_elements = member_type.array[0]; if (member_type.basetype == spirv_cross::SPIRType::Struct) { - size_t stride = compiler.type_struct_member_array_stride(type, index); - size_t struct_size = compiler.get_declared_struct_size(member_type); + auto stride = compiler.type_struct_member_array_stride(type, index); + auto struct_size = compiler.get_declared_struct_size(member_type); if (descriptorType == nap::EDescriptorType::Storage) { @@ -603,21 +602,18 @@ static bool addShaderVariablesRecursive(nap::ShaderVariableStructDeclaration& pa // of the ShaderVariableStructBufferDeclaration along with the element stride and count. All that matters is the size of the struct element, // which is already resolved by SPIR-V. Therefore, we do not have to traverse the struct recursively here. - std::unique_ptr buffer_declaration = std::make_unique(name, absoluteOffset, member_size, stride, num_elements); - std::unique_ptr struct_declaration = std::make_unique(name, parentStruct.mDescriptorType, absoluteOffset, struct_size); - buffer_declaration->mElement = std::move(struct_declaration); + auto buffer_declaration = std::make_unique(name, absoluteOffset, member_size, stride, num_elements); + buffer_declaration->mElement = std::make_unique(name, parentStruct.mDescriptorType, absoluteOffset, struct_size); parentStruct.mMembers.emplace_back(std::move(buffer_declaration)); } else if (descriptorType == nap::EDescriptorType::Uniform) { - std::unique_ptr array_declaration = std::make_unique(name, absoluteOffset, member_size); - + auto array_declaration = std::make_unique(name, absoluteOffset, member_size); for (int array_index = 0; array_index < num_elements; ++array_index) { - std::string array_path = nap::utility::stringFormat("%s[%d]", full_path.c_str(), array_index); - - std::unique_ptr struct_declaration = std::make_unique(name, parentStruct.mDescriptorType, absoluteOffset, struct_size); + auto array_path = nap::utility::stringFormat("%s[%d]", full_path.c_str(), array_index); + auto struct_declaration = std::make_unique(name, parentStruct.mDescriptorType, absoluteOffset, struct_size); if (!addShaderVariablesRecursive(*struct_declaration, compiler, member_type, absoluteOffset, array_path, descriptorType, errorState)) return false; @@ -629,23 +625,20 @@ static bool addShaderVariablesRecursive(nap::ShaderVariableStructDeclaration& pa } else { - size_t stride = compiler.type_struct_member_array_stride(type, index); - - nap::EShaderVariableValueType element_type = getShaderVariableValueType(member_type); + auto stride = compiler.type_struct_member_array_stride(type, index); + auto element_type = getShaderVariableValueType(member_type); if (!errorState.check(element_type != nap::EShaderVariableValueType::Unknown, "Encountered unknown uniform type")) return false; - std::unique_ptr array_declaration = std::make_unique(name, absoluteOffset, member_size, stride, element_type, num_elements); - parentStruct.mMembers.emplace_back(std::move(array_declaration)); + parentStruct.mMembers.emplace_back(std::make_unique(name, absoluteOffset, member_size, stride, element_type, num_elements)); } } else { if (member_type.basetype == spirv_cross::SPIRType::Struct) { - size_t struct_size = compiler.get_declared_struct_size(member_type); - - std::unique_ptr struct_declaration = std::make_unique(name, parentStruct.mDescriptorType, absoluteOffset, struct_size); + auto struct_size = compiler.get_declared_struct_size(member_type); + auto struct_declaration = std::make_unique(name, parentStruct.mDescriptorType, absoluteOffset, struct_size); if (!addShaderVariablesRecursive(*struct_declaration, compiler, member_type, absoluteOffset, name, descriptorType, errorState)) return false; @@ -653,12 +646,11 @@ static bool addShaderVariablesRecursive(nap::ShaderVariableStructDeclaration& pa } else { - nap::EShaderVariableValueType value_type = getShaderVariableValueType(member_type); + auto value_type = getShaderVariableValueType(member_type); if (!errorState.check(value_type != nap::EShaderVariableValueType::Unknown, "Encountered unknown uniform type")) return false; - std::unique_ptr value_declaration = std::make_unique(name, absoluteOffset, member_size, value_type); - parentStruct.mMembers.emplace_back(std::move(value_declaration)); + parentStruct.mMembers.emplace_back(std::make_unique(name, absoluteOffset, member_size, value_type)); } } } diff --git a/system_modules/naprender/src/snapshotrendertarget.cpp b/system_modules/naprender/src/snapshotrendertarget.cpp index 79309b600..803676240 100644 --- a/system_modules/naprender/src/snapshotrendertarget.cpp +++ b/system_modules/naprender/src/snapshotrendertarget.cpp @@ -215,8 +215,9 @@ namespace nap framebuffer_info.renderPass = mRenderPass; // Bind textures as attachments - for (int i = 0; i < num_cells; i++) { - std::array attachments{ mSnapshot->mColorTextures[i]->getHandle().getView(), mDepthImage.getView() }; + for (int i = 0; i < num_cells; i++) + { + std::array attachments{ std::as_const(*mSnapshot->mColorTextures[i]).getHandle().getView(), mDepthImage.getView() }; framebuffer_info.pAttachments = attachments.data(); // Create framebuffer @@ -237,8 +238,9 @@ namespace nap framebuffer_info.attachmentCount = 3; framebuffer_info.renderPass = mRenderPass; - for (int i = 0; i < num_cells; i++) { - std::array attachments{ mColorImage.getView(), mDepthImage.getView(), mSnapshot->mColorTextures[i]->getHandle().getView() }; + for (int i = 0; i < num_cells; i++) + { + std::array attachments{ mColorImage.getView(), mDepthImage.getView(), std::as_const(*mSnapshot->mColorTextures[i]).getHandle().getView() }; framebuffer_info.pAttachments = attachments.data(); // Create a framebuffer that links the cell target texture to the appropriate resolved color attachment @@ -255,9 +257,10 @@ namespace nap glm::ivec2 size = getBufferSize(); const RGBAColorFloat& clear_color = mSnapshot->mClearColor; - std::array clearValues = {}; + std::array clearValues = {}; clearValues[0].color = { clear_color[0], clear_color[1], clear_color[2], clear_color[3] }; clearValues[1].depthStencil = { 1.0f, 0 }; + clearValues[2].color = { clear_color[0], clear_color[1], clear_color[2], clear_color[3] }; // Setup render pass VkRenderPassBeginInfo renderPassInfo = {}; @@ -293,6 +296,10 @@ namespace nap void SnapshotRenderTarget::endRendering() { vkCmdEndRenderPass(mRenderService->getCurrentCommandBuffer()); + + // Sync image data with render pass final layout + for (auto& tex : mSnapshot->mColorTextures) + tex->syncLayout(); } diff --git a/system_modules/naprender/src/texture.cpp b/system_modules/naprender/src/texture.cpp index 892686e20..01a1097d3 100644 --- a/system_modules/naprender/src/texture.cpp +++ b/system_modules/naprender/src/texture.cpp @@ -130,8 +130,8 @@ namespace nap // Get image ready for clear, applied to all mipmap layers VkImageAspectFlags aspect = mDescriptor.getChannels() == ESurfaceChannels::D ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; - utility::transitionImageLayout(commandBuffer, getHandle().mImage, - getHandle().mCurrentLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + utility::transitionImageLayout(commandBuffer, + getHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, srcMask, dstMask, srcStage, dstStage, 0, getMipLevels(), @@ -151,15 +151,12 @@ namespace nap } // Transition image layout - utility::transitionImageLayout(commandBuffer, getHandle().mImage, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, getTargetLayout(), + utility::transitionImageLayout(commandBuffer, + getHandle(), getTargetLayout(), VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, getMipLevels(), aspect); - - // We store the last image layout, which is used as input for a subsequent upload - getHandle().mCurrentLayout = getTargetLayout(); } @@ -355,8 +352,8 @@ namespace nap // Get image ready for copy, applied to all mipmap layers VkImageAspectFlags aspect = mDescriptor.getChannels() == ESurfaceChannels::D ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; - utility::transitionImageLayout(commandBuffer, mImageData.mImage, - mImageData.mCurrentLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + utility::transitionImageLayout(commandBuffer, + getHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, srcMask, dstMask, srcStage, dstStage, 0, mMipLevels, @@ -368,21 +365,18 @@ namespace nap // Generate mip maps, if we do that we don't have to transition the image layout anymore, this is handled by createMipmaps. if (mMipLevels > 1) { - utility::createMipmaps(commandBuffer, mImageData.mImage, mFormat, getTargetLayout(), aspect, mDescriptor.mWidth, mDescriptor.mHeight, mMipLevels); + utility::createMipmaps(commandBuffer, mImageData, mFormat, getTargetLayout(), aspect, mDescriptor.mWidth, mDescriptor.mHeight, mMipLevels); } else { - utility::transitionImageLayout(commandBuffer, mImageData.mImage, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, getTargetLayout(), + utility::transitionImageLayout(commandBuffer, + getHandle(), getTargetLayout(), VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 1, aspect); } - // We store the last image layout, which is used as input for a subsequent upload - mImageData.mCurrentLayout = getTargetLayout(); - // Destroy staging buffer when usage is static // This queues the vulkan staging resource for destruction, executed by the render service at the appropriate time. // Explicitly release the handle, so it's not deleted twice. @@ -409,8 +403,8 @@ namespace nap // Transition for copy VkImageAspectFlags aspect = mDescriptor.getChannels() == ESurfaceChannels::D ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; - utility::transitionImageLayout(commandBuffer, mImageData.mImage, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + utility::transitionImageLayout(commandBuffer, + getHandle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 1, @@ -420,8 +414,8 @@ namespace nap copyImageToBuffer(commandBuffer, mImageData.mImage, buffer.mBuffer, aspect, mDescriptor.mWidth, mDescriptor.mHeight); // Transition back to shader usage - utility::transitionImageLayout(commandBuffer, mImageData.mImage, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + utility::transitionImageLayout(commandBuffer, + getHandle(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 1, diff --git a/system_modules/naprender/src/texture.h b/system_modules/naprender/src/texture.h index 170e65359..d220c2221 100644 --- a/system_modules/naprender/src/texture.h +++ b/system_modules/naprender/src/texture.h @@ -7,6 +7,7 @@ // Local Includes #include "surfacedescriptor.h" #include "renderutils.h" +#include "textureutils.h" // External Includes #include @@ -128,6 +129,9 @@ namespace nap class NAPAPI Texture2D : public Texture { friend class RenderService; + friend void utility::blit(VkCommandBuffer, Texture2D&, Texture2D&); + friend void utility::copy(VkCommandBuffer, Texture2D&, Texture2D&); + RTTI_ENABLE(Texture) public: Texture2D(Core& core); diff --git a/system_modules/naprender/src/textureutils.cpp b/system_modules/naprender/src/textureutils.cpp index ab8fb0093..2d1204deb 100644 --- a/system_modules/naprender/src/textureutils.cpp +++ b/system_modules/naprender/src/textureutils.cpp @@ -7,6 +7,7 @@ // Local Includes #include "textureutils.h" +#include "texture.h" namespace nap { @@ -78,72 +79,89 @@ namespace nap } - void transitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, - VkImageLayout oldLayout, VkImageLayout newLayout, + static void transitionImageLayoutInternal(VkCommandBuffer commandBuffer, VkImage image, + VkImageLayout oldLayout, VkImageLayout newLayout, + VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, + VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage, + uint mipLevel, uint mipLevelCount, + uint layer, uint layerCount, VkImageAspectFlags aspect) + { + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = oldLayout; + barrier.newLayout = newLayout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + barrier.subresourceRange.aspectMask = aspect; + barrier.subresourceRange.baseMipLevel = mipLevel; + barrier.subresourceRange.levelCount = mipLevelCount; + barrier.subresourceRange.baseArrayLayer = layer; + barrier.subresourceRange.layerCount = layerCount; + barrier.srcAccessMask = srcAccessMask; + barrier.dstAccessMask = dstAccessMask; + vkCmdPipelineBarrier(commandBuffer, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); + } + + + void transitionImageLayout(VkCommandBuffer commandBuffer, + ImageData& image, VkImageLayout newLayout, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage, uint mipLevel, uint mipLevelCount, VkImageAspectFlags aspect) { - transitionImageLayout(commandBuffer, image, oldLayout, newLayout, srcAccessMask, dstAccessMask, srcStage, dstStage, mipLevel, mipLevelCount, 0, VK_REMAINING_ARRAY_LAYERS, aspect); + transitionImageLayoutInternal(commandBuffer, image.getImage(), image.getLayout(), newLayout, srcAccessMask, dstAccessMask, srcStage, dstStage, mipLevel, mipLevelCount, 0, VK_REMAINING_ARRAY_LAYERS, aspect); + + // Update image layout + image.mCurrentLayout = newLayout; } - void transitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, - VkImageLayout oldLayout, VkImageLayout newLayout, + void transitionImageLayout(VkCommandBuffer commandBuffer, + ImageData& image, VkImageLayout newLayout, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage, uint mipLevel, uint mipLevelCount, uint layer, uint layerCount, VkImageAspectFlags aspect) { - VkImageMemoryBarrier barrier = {}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = oldLayout; - barrier.newLayout = newLayout; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = image; - barrier.subresourceRange.aspectMask = aspect; - barrier.subresourceRange.baseMipLevel = mipLevel; - barrier.subresourceRange.levelCount = mipLevelCount; - barrier.subresourceRange.baseArrayLayer = layer; - barrier.subresourceRange.layerCount = layerCount; - barrier.srcAccessMask = srcAccessMask; - barrier.dstAccessMask = dstAccessMask; - vkCmdPipelineBarrier(commandBuffer, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); + transitionImageLayoutInternal(commandBuffer, image.getImage(), image.getLayout(), newLayout, srcAccessMask, dstAccessMask, srcStage, dstStage, mipLevel, mipLevelCount, layer, layerCount, aspect); + + // Update image layout + image.mCurrentLayout = newLayout; } - void createMipmaps(VkCommandBuffer buffer, VkImage image, VkFormat imageFormat, VkImageLayout targetLayout, VkImageAspectFlags aspect, uint32 texWidth, uint32 texHeight, uint32 mipLevels) + void createMipmaps(VkCommandBuffer buffer, ImageData& image, VkFormat imageFormat, VkImageLayout targetLayout, VkImageAspectFlags aspect, uint32 texWidth, uint32 texHeight, uint32 mipLevels) { return createMipmaps(buffer, image, imageFormat, targetLayout, aspect, texWidth, texHeight, mipLevels, 0, 1); } - void createMipmaps(VkCommandBuffer buffer, VkImage image, VkFormat imageFormat, VkImageLayout targetLayout, VkImageAspectFlags aspect, uint32 texWidth, uint32 texHeight, uint32 mipLevels, uint layer, uint layerCount) + void createMipmaps(VkCommandBuffer buffer, ImageData& image, VkFormat imageFormat, VkImageLayout targetLayout, VkImageAspectFlags aspect, uint32 texWidth, uint32 texHeight, uint32 mipLevels, uint layer, uint layerCount) { - int32 mipWidth = static_cast(texWidth); - int32 mipHeight = static_cast(texHeight); + auto mip_width = static_cast(texWidth); + auto mip_height = static_cast(texHeight); for (uint32 i = 1; i < mipLevels; i++) { - // Prepare LOD for blit operation - transitionImageLayout(buffer, image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + // Prepare src LOD for blit operation + transitionImageLayoutInternal(buffer, image.getImage(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - i - 1, 1, layer, layerCount, + i-1, 1, layer, layerCount, aspect); // Create blit structure VkImageBlit blit{}; blit.srcOffsets[0] = { 0, 0, 0 }; - blit.srcOffsets[1] = { mipWidth, mipHeight, 1 }; + blit.srcOffsets[1] = {mip_width, mip_height, 1 }; blit.srcSubresource.aspectMask = aspect; - blit.srcSubresource.mipLevel = i - 1; + blit.srcSubresource.mipLevel = i-1; blit.srcSubresource.baseArrayLayer = layer; blit.srcSubresource.layerCount = layerCount; blit.dstOffsets[0] = { 0, 0, 0 }; - blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1 }; + blit.dstOffsets[1] = {mip_width > 1 ? mip_width / 2 : 1, mip_height > 1 ? mip_height / 2 : 1, 1 }; blit.dstSubresource.aspectMask = aspect; blit.dstSubresource.mipLevel = i; blit.dstSubresource.baseArrayLayer = layer; @@ -151,52 +169,52 @@ namespace nap // Blit vkCmdBlitImage(buffer, - image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + image.getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + image.getImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR); - // Prepare LOD for shader read - transitionImageLayout(buffer, image, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, targetLayout, + // Prepare src LOD for shader read + transitionImageLayoutInternal(buffer, image.getImage(), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, targetLayout, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - i - 1, 1, layer, layerCount, + i-1, 1, layer, layerCount, aspect); - if (mipWidth > 1) mipWidth /= 2; - if (mipHeight > 1) mipHeight /= 2; + if (mip_width > 1) mip_width /= 2; + if (mip_height > 1) mip_height /= 2; } // Prepare final LOD for shader read - transitionImageLayout(buffer, image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, targetLayout, + transitionImageLayoutInternal(buffer, image.getImage(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, targetLayout, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - mipLevels - 1, 1, layer, layerCount, + mipLevels-1, 1, layer, layerCount, aspect); } - void blit(VkCommandBuffer commandBuffer, const Texture2D& srcTexture, const Texture2D& dstTexture) + void blit(VkCommandBuffer commandBuffer, Texture2D& srcTexture, Texture2D& dstTexture) { // Transition to transfer src - VkImageLayout src_tex_layout = srcTexture.getHandle().getLayout(); + const auto src_tex_layout = srcTexture.getHandle().getLayout(); if (src_tex_layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - transitionImageLayout(commandBuffer, srcTexture.getHandle().getImage(), - src_tex_layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + transitionImageLayout(commandBuffer, + srcTexture.getHandle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT); } // Transition to transfer dst - VkImageLayout dst_tex_layout = dstTexture.getHandle().getLayout(); + const auto dst_tex_layout = dstTexture.getHandle().getLayout(); if (dst_tex_layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - transitionImageLayout(commandBuffer, dstTexture.getHandle().getImage(), - dst_tex_layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + transitionImageLayout(commandBuffer, + dstTexture.getHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT); @@ -206,10 +224,7 @@ namespace nap VkImageBlit blit{}; blit.srcOffsets[0] = { 0, 0, 0 }; blit.srcOffsets[1] = { srcTexture.getWidth(), srcTexture.getHeight(), 1 }; - blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blit.srcSubresource.mipLevel = 0; - blit.srcSubresource.baseArrayLayer = 0; - blit.srcSubresource.layerCount = 1; + blit.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }; blit.dstOffsets[0] = { 0, 0, 0 }; blit.dstOffsets[1] = { dstTexture.getWidth(), dstTexture.getHeight(), 1 }; @@ -224,16 +239,73 @@ namespace nap dstTexture.getHandle().getImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR); - // Transition to shader read - transitionImageLayout(commandBuffer, srcTexture.getHandle().getImage(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + // Restore initial layout + transitionImageLayout(commandBuffer, + srcTexture.getHandle(), src_tex_layout, + VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + 0, 1, VK_IMAGE_ASPECT_COLOR_BIT); + + // Restore initial layout + transitionImageLayout(commandBuffer, + dstTexture.getHandle(), dst_tex_layout, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + 0, 1, VK_IMAGE_ASPECT_COLOR_BIT); + } + + + void copy(VkCommandBuffer commandBuffer, Texture2D& srcTexture, Texture2D& dstTexture) + { + assert(srcTexture.getSize() == dstTexture.getSize()); + assert(srcTexture.getFormat() == dstTexture.getFormat()); + + // Transition to transfer src + const auto src_tex_layout = srcTexture.getHandle().getLayout(); + if (src_tex_layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) + { + transitionImageLayout(commandBuffer, + srcTexture.getHandle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, 1, VK_IMAGE_ASPECT_COLOR_BIT); + } + + // Transition to transfer dst + const auto dst_tex_layout = dstTexture.getHandle().getLayout(); + if (dst_tex_layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + { + transitionImageLayout(commandBuffer, + dstTexture.getHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, 1, VK_IMAGE_ASPECT_COLOR_BIT); + } + + // Create blit structure + VkImageCopy region = {}; + region.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }; + region.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }; + region.srcOffset = { 0, 0, 0 }; + region.dstOffset = { 0, 0, 0 }; + region.extent = { static_cast(srcTexture.getWidth()), static_cast(srcTexture.getHeight()), 1 }; + + // Blit to output + vkCmdCopyImage(commandBuffer, + srcTexture.getHandle().getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + dstTexture.getHandle().getImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ®ion); + + // Restore initial layout + transitionImageLayout(commandBuffer, + srcTexture.getHandle(), src_tex_layout, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT); - // Transition to shader read - transitionImageLayout(commandBuffer, dstTexture.getHandle().getImage(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + // Restore initial layout + transitionImageLayout(commandBuffer, + dstTexture.getHandle(), dst_tex_layout, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT); diff --git a/system_modules/naprender/src/textureutils.h b/system_modules/naprender/src/textureutils.h index 981a8516a..18dce8f18 100644 --- a/system_modules/naprender/src/textureutils.h +++ b/system_modules/naprender/src/textureutils.h @@ -7,14 +7,17 @@ // External Includes #include #include -#include #include +#include // Local Includes #include "surfacedescriptor.h" namespace nap { + // Forward declaration + class Texture2D; + namespace utility { /** @@ -30,33 +33,42 @@ namespace nap /** * Transition image to a new layout using an image barrier. */ - void NAPAPI transitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, + void NAPAPI transitionImageLayout(VkCommandBuffer commandBuffer, ImageData& image, VkImageLayout newLayout, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage, uint mipLevel, uint mipLevelCount, VkImageAspectFlags aspect); /** * Transition image to a new layout using an image barrier. */ - void NAPAPI transitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, + void NAPAPI transitionImageLayout(VkCommandBuffer commandBuffer, ImageData& image, VkImageLayout newLayout, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage, uint mipLevel, uint mipLevelCount, uint layer, uint layerCount, VkImageAspectFlags aspect); /** * Creates mip maps for the specified Vulkan image. */ - void NAPAPI createMipmaps(VkCommandBuffer buffer, VkImage image, VkFormat imageFormat, VkImageLayout targetLayout, VkImageAspectFlags aspect, uint32 texWidth, uint32 texHeight, uint32 mipLevels); + void NAPAPI createMipmaps(VkCommandBuffer buffer, ImageData& image, VkFormat imageFormat, VkImageLayout targetLayout, VkImageAspectFlags aspect, uint32 texWidth, uint32 texHeight, uint32 mipLevels); /** * Creates mip maps for the specified Vulkan image. */ - void NAPAPI createMipmaps(VkCommandBuffer buffer, VkImage image, VkFormat imageFormat, VkImageLayout targetLayout, VkImageAspectFlags aspect, uint32 texWidth, uint32 texHeight, uint32 mipLevels, uint layer, uint layerCount); + void NAPAPI createMipmaps(VkCommandBuffer buffer, ImageData& image, VkFormat imageFormat, VkImageLayout targetLayout, VkImageAspectFlags aspect, uint32 texWidth, uint32 texHeight, uint32 mipLevels, uint layer, uint layerCount); /** * Pushes a full-size blit to the command buffer. Must be called inside a render pass, in onDraw(). - * Assumes a color texture without mip-maps. The layouts of srcTexture and dstTexture are - * transferred to SHADER_READ after the blit operation. + * Assumes a color texture without mip-maps. + * @param commandBuffer the command buffer to push the blit operation to + * @param srcTexture the source texture + * @param dstTexture the destination texture + */ + void NAPAPI blit(VkCommandBuffer commandBuffer, Texture2D& srcTexture, Texture2D& dstTexture); + + /** + * Pushes a full-size color copy to the command buffer. Must be called inside a render pass, + * in onDraw(). Assumes a color texture without mip-maps. Asserts the src and dest texture + * are exactly the same size and format. * @param commandBuffer the command buffer to push the blit operation to * @param srcTexture the source texture * @param dstTexture the destination texture */ - void NAPAPI blit(VkCommandBuffer commandBuffer, const Texture2D& srcTexture, const Texture2D& dstTexture); + void NAPAPI copy(VkCommandBuffer commandBuffer, Texture2D& srcTexture, Texture2D& dstTexture); } } diff --git a/system_modules/naprenderadvanced/src/cuberendertarget.cpp b/system_modules/naprenderadvanced/src/cuberendertarget.cpp index 22c6ee916..d183ea3b9 100644 --- a/system_modules/naprenderadvanced/src/cuberendertarget.cpp +++ b/system_modules/naprenderadvanced/src/cuberendertarget.cpp @@ -158,8 +158,8 @@ namespace nap // We transition the layout of the depth attachment from UNDEFINED to DEPTH_STENCIL_ATTACHMENT_OPTIMAL, once in the first pass if (mIsFirstPass) { - utility::transitionImageLayout(mRenderService->getCurrentCommandBuffer(), mDepthImage.mImage, - mDepthImage.mCurrentLayout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + utility::transitionImageLayout(mRenderService->getCurrentCommandBuffer(), + mDepthImage, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, @@ -254,6 +254,9 @@ namespace nap endRendering(); } + // Sync image data with render pass final layout + mCubeTexture->syncLayout(); + // Update mip maps if (mUpdateLODs && mCubeTexture->getMipLevels() > 1) { @@ -262,14 +265,14 @@ namespace nap VkImageAspectFlags aspect = is_depth ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; // Layout transition to TRANSFER_DST to setup the mip map blit operation - utility::transitionImageLayout(mRenderService->getCurrentCommandBuffer(), mCubeTexture->getHandle().getImage(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + utility::transitionImageLayout(mRenderService->getCurrentCommandBuffer(), + mCubeTexture->getHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, mCubeTexture->getMipLevels(), 0, TextureCube::layerCount, VK_IMAGE_ASPECT_COLOR_BIT); - utility::createMipmaps(mRenderService->getCurrentCommandBuffer(), mCubeTexture->getHandle().getImage(), + utility::createMipmaps(mRenderService->getCurrentCommandBuffer(), mCubeTexture->getHandle(), mCubeTexture->getFormat(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, aspect, mCubeTexture->getWidth(), mCubeTexture->getHeight(), mCubeTexture->getMipLevels(), 0, TextureCube::layerCount ); diff --git a/system_modules/naprenderadvanced/src/cuberendertarget.h b/system_modules/naprenderadvanced/src/cuberendertarget.h index 081d7e887..4f6f5b088 100644 --- a/system_modules/naprenderadvanced/src/cuberendertarget.h +++ b/system_modules/naprenderadvanced/src/cuberendertarget.h @@ -191,7 +191,7 @@ namespace nap RenderTextureCube::EFormat mColorFormat = RenderTextureCube::EFormat::RGBA8; ///< Property: 'ColorFormat' the cube texture color format. DepthRenderTextureCube::EDepthFormat mDepthFormat = DepthRenderTextureCube::EDepthFormat::D16; ///< Property: 'DepthFormat' the cube texture depth format. - ResourcePtr mCubeTexture; ///< Property: 'CubeTexture' cube texture to render to. + ResourcePtr mCubeTexture; ///< Property: 'CubeTexture' cube texture to render to. private: /** diff --git a/system_modules/naprenderadvanced/src/renderfrustumcomponent.cpp b/system_modules/naprenderadvanced/src/renderfrustumcomponent.cpp index dd9bfd357..c3b5c5ce1 100644 --- a/system_modules/naprenderadvanced/src/renderfrustumcomponent.cpp +++ b/system_modules/naprenderadvanced/src/renderfrustumcomponent.cpp @@ -140,9 +140,9 @@ namespace nap auto& positions = mFrustumMesh.getMeshInstance().getOrCreateAttribute(vertexid::position).getData(); assert(mFrustumMesh.getNormalizedLineBox().size() == positions.size()); - if (mCamera->get_type().is_derived_from(RTTI_OF(OrthoCameraComponentInstance))) + const auto inv_proj_matrix = glm::inverse(mCamera->getProjectionMatrix()); + if (mCamera->get_type().is_derived_from(RTTI_OF(OrthoCameraComponentInstance))) { - auto inv_proj_matrix = glm::inverse(mCamera->getProjectionMatrix()); for (uint i = 0; i < mFrustumMesh.getNormalizedLineBox().size(); i++) { auto view_edge = inv_proj_matrix * glm::vec4(mFrustumMesh.getNormalizedLineBox()[i], 1.0f); @@ -151,7 +151,6 @@ namespace nap } else if (mCamera->get_type().is_derived_from(RTTI_OF(PerspCameraComponentInstance))) { - auto inv_proj_matrix = glm::inverse(mCamera->getProjectionMatrix()); for (uint i = 0; i < mFrustumMesh.getNormalizedLineBox().size(); i++) { auto view_edge = inv_proj_matrix * glm::vec4(mFrustumMesh.getNormalizedLineBox()[i], 1.0f);