diff options
Diffstat (limited to 'tools/gfx/vulkan/vk-command-encoder.cpp')
| -rw-r--r-- | tools/gfx/vulkan/vk-command-encoder.cpp | 1499 |
1 files changed, 1499 insertions, 0 deletions
diff --git a/tools/gfx/vulkan/vk-command-encoder.cpp b/tools/gfx/vulkan/vk-command-encoder.cpp new file mode 100644 index 000000000..00915e0c4 --- /dev/null +++ b/tools/gfx/vulkan/vk-command-encoder.cpp @@ -0,0 +1,1499 @@ +// vk-command-encoder.cpp +#include "vk-command-encoder.h" + +#include "vk-buffer.h" +#include "vk-command-buffer.h" +#include "vk-query.h" +#include "vk-render-pass.h" +#include "vk-resource-views.h" +#include "vk-shader-object.h" +#include "vk-shader-program.h" +#include "vk-shader-table.h" +#include "vk-texture.h" +#include "vk-transient-heap.h" + +#include "vk-helper-functions.h" + +namespace gfx +{ + +using namespace Slang; + +namespace vk +{ + +int PipelineCommandEncoder::getBindPointIndex(VkPipelineBindPoint bindPoint) +{ + switch (bindPoint) + { + case VK_PIPELINE_BIND_POINT_GRAPHICS: + return 0; + case VK_PIPELINE_BIND_POINT_COMPUTE: + return 1; + case VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR: + return 2; + default: + assert(!"unknown pipeline type."); + return -1; + } +} + +void PipelineCommandEncoder::init(CommandBufferImpl* commandBuffer) +{ + m_commandBuffer = commandBuffer; + m_device = commandBuffer->m_renderer; + m_vkCommandBuffer = m_commandBuffer->m_commandBuffer; + m_api = &m_commandBuffer->m_renderer->m_api; +} + +void PipelineCommandEncoder::endEncodingImpl() +{ + for (auto& pipeline : m_boundPipelines) + pipeline = VK_NULL_HANDLE; +} + +void PipelineCommandEncoder::_uploadBufferData( + VkCommandBuffer commandBuffer, + TransientResourceHeapImpl* transientHeap, + BufferResourceImpl* buffer, + Offset offset, + Size size, + void* data) +{ + auto& api = buffer->m_renderer->m_api; + IBufferResource* stagingBuffer = nullptr; + Offset stagingBufferOffset = 0; + transientHeap->allocateStagingBuffer( + size, stagingBuffer, stagingBufferOffset, MemoryType::Upload); + + BufferResourceImpl* stagingBufferImpl = static_cast<BufferResourceImpl*>(stagingBuffer); + + void* mappedData = nullptr; + SLANG_VK_CHECK(api.vkMapMemory( + api.m_device, + stagingBufferImpl->m_buffer.m_memory, + 0, + stagingBufferOffset + size, + 0, + &mappedData)); + memcpy((char*)mappedData + stagingBufferOffset, data, size); + api.vkUnmapMemory(api.m_device, stagingBufferImpl->m_buffer.m_memory); + + // Copy from staging buffer to real buffer + VkBufferCopy copyInfo = {}; + copyInfo.size = size; + copyInfo.dstOffset = offset; + copyInfo.srcOffset = stagingBufferOffset; + api.vkCmdCopyBuffer( + commandBuffer, + stagingBufferImpl->m_buffer.m_buffer, + buffer->m_buffer.m_buffer, + 1, + ©Info); +} + +void PipelineCommandEncoder::uploadBufferDataImpl( + IBufferResource* buffer, Offset offset, Size size, void* data) +{ + m_vkPreCommandBuffer = m_commandBuffer->getPreCommandBuffer(); + _uploadBufferData( + m_vkPreCommandBuffer, + m_commandBuffer->m_transientHeap.get(), + static_cast<BufferResourceImpl*>(buffer), + offset, + size, + data); +} + +Result PipelineCommandEncoder::bindRootShaderObjectImpl(VkPipelineBindPoint bindPoint) +{ + // Obtain specialized root layout. + auto rootObjectImpl = &m_commandBuffer->m_rootObject; + + auto specializedLayout = rootObjectImpl->getSpecializedLayout(); + if (!specializedLayout) + return SLANG_FAIL; + + // We will set up the context required when binding shader objects + // to the pipeline. Note that this is mostly just being packaged + // together to minimize the number of parameters that have to + // be dealt with in the complex recursive call chains. + // + RootBindingContext context; + context.pipelineLayout = specializedLayout->m_pipelineLayout; + context.device = m_device; + context.descriptorSetAllocator = &m_commandBuffer->m_transientHeap->m_descSetAllocator; + context.pushConstantRanges = specializedLayout->getAllPushConstantRanges().getArrayView(); + + // The context includes storage for the descriptor sets we will bind, + // and the number of sets we need to make space for is determined + // by the specialized program layout. + // + List<VkDescriptorSet> descriptorSetsStorage; + auto descriptorSetCount = specializedLayout->getTotalDescriptorSetCount(); + + descriptorSetsStorage.setCount(descriptorSetCount); + auto descriptorSets = descriptorSetsStorage.getBuffer(); + + context.descriptorSets = descriptorSets; + + // We kick off recursive binding of shader objects to the pipeline (plus + // the state in `context`). + // + // Note: this logic will directly write any push-constant ranges needed, + // and will also fill in any descriptor sets. Currently it does not + // *bind* the descriptor sets it fills in. + // + // TODO: It could probably bind the descriptor sets as well. + // + rootObjectImpl->bindAsRoot(this, context, specializedLayout); + + // Once we've filled in all the descriptor sets, we bind them + // to the pipeline at once. + // + if (descriptorSetCount > 0) + { + m_device->m_api.vkCmdBindDescriptorSets( + m_commandBuffer->m_commandBuffer, + bindPoint, + specializedLayout->m_pipelineLayout, + 0, + (uint32_t)descriptorSetCount, + descriptorSets, + 0, + nullptr); + } + + return SLANG_OK; +} + +Result PipelineCommandEncoder::setPipelineStateImpl( + IPipelineState* state, IShaderObject** outRootObject) +{ + m_currentPipeline = static_cast<PipelineStateImpl*>(state); + SLANG_RETURN_ON_FAIL(m_commandBuffer->m_rootObject.init( + m_commandBuffer->m_renderer, + m_currentPipeline->getProgram<ShaderProgramImpl>()->m_rootObjectLayout)); + *outRootObject = &m_commandBuffer->m_rootObject; + return SLANG_OK; +} + +Result PipelineCommandEncoder::setPipelineStateWithRootObjectImpl( + IPipelineState* state, IShaderObject* inObject) +{ + IShaderObject* rootObject = nullptr; + SLANG_RETURN_ON_FAIL(setPipelineStateImpl(state, &rootObject)); + static_cast<ShaderObjectBase*>(rootObject) + ->copyFrom(inObject, m_commandBuffer->m_transientHeap); + return SLANG_OK; +} + +void PipelineCommandEncoder::bindRenderState(VkPipelineBindPoint pipelineBindPoint) +{ + auto& api = *m_api; + + // Get specialized pipeline state and bind it. + // + RefPtr<PipelineStateBase> newPipeline; + m_device->maybeSpecializePipeline( + m_currentPipeline, &m_commandBuffer->m_rootObject, newPipeline); + PipelineStateImpl* newPipelineImpl = static_cast<PipelineStateImpl*>(newPipeline.Ptr()); + + newPipelineImpl->ensureAPIPipelineStateCreated(); + m_currentPipeline = newPipelineImpl; + + bindRootShaderObjectImpl(pipelineBindPoint); + + auto pipelineBindPointId = getBindPointIndex(pipelineBindPoint); + if (m_boundPipelines[pipelineBindPointId] != newPipelineImpl->m_pipeline) + { + api.vkCmdBindPipeline(m_vkCommandBuffer, pipelineBindPoint, newPipelineImpl->m_pipeline); + m_boundPipelines[pipelineBindPointId] = newPipelineImpl->m_pipeline; + } +} + +void ResourceCommandEncoder::copyBuffer( + IBufferResource* dst, Offset dstOffset, IBufferResource* src, Offset srcOffset, Size size) +{ + auto& vkAPI = m_commandBuffer->m_renderer->m_api; + + auto dstBuffer = static_cast<BufferResourceImpl*>(dst); + auto srcBuffer = static_cast<BufferResourceImpl*>(src); + + VkBufferCopy copyRegion; + copyRegion.dstOffset = dstOffset; + copyRegion.srcOffset = srcOffset; + copyRegion.size = size; + + // Note: Vulkan puts the source buffer first in the copy + // command, going against the dominant tradition for copy + // operations in C/C++. + // + vkAPI.vkCmdCopyBuffer( + m_commandBuffer->m_commandBuffer, + srcBuffer->m_buffer.m_buffer, + dstBuffer->m_buffer.m_buffer, + /* regionCount: */ 1, + ©Region); +} + +void ResourceCommandEncoder::uploadBufferData( + IBufferResource* buffer, Offset offset, Size size, void* data) +{ + PipelineCommandEncoder::_uploadBufferData( + m_commandBuffer->m_commandBuffer, + m_commandBuffer->m_transientHeap.get(), + static_cast<BufferResourceImpl*>(buffer), + offset, + size, + data); +} + +void ResourceCommandEncoder::textureBarrier( + GfxCount count, ITextureResource* const* textures, ResourceState src, ResourceState dst) +{ + ShortList<VkImageMemoryBarrier, 16> barriers; + + for (GfxIndex i = 0; i < count; i++) + { + auto image = static_cast<TextureResourceImpl*>(textures[i]); + auto desc = image->getDesc(); + + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.image = image->m_image; + barrier.oldLayout = translateImageLayout(src); + barrier.newLayout = translateImageLayout(dst); + barrier.subresourceRange.aspectMask = getAspectMaskFromFormat(VulkanUtil::getVkFormat(desc->format)); + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + barrier.srcAccessMask = calcAccessFlags(src); + barrier.dstAccessMask = calcAccessFlags(dst); + barriers.add(barrier); + } + + VkPipelineStageFlagBits srcStage = calcPipelineStageFlags(src, true); + VkPipelineStageFlagBits dstStage = calcPipelineStageFlags(dst, false); + + auto& vkApi = m_commandBuffer->m_renderer->m_api; + vkApi.vkCmdPipelineBarrier( + m_commandBuffer->m_commandBuffer, + srcStage, + dstStage, + 0, + 0, + nullptr, + 0, + nullptr, + (uint32_t)count, + barriers.getArrayView().getBuffer()); +} + +// TODO: Change size_t to Count? +void ResourceCommandEncoder::bufferBarrier( + GfxCount count, IBufferResource* const* buffers, ResourceState src, ResourceState dst) +{ + List<VkBufferMemoryBarrier> barriers; + barriers.reserve(count); + + for (GfxIndex i = 0; i < count; i++) + { + auto bufferImpl = static_cast<BufferResourceImpl*>(buffers[i]); + + VkBufferMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + barrier.srcAccessMask = calcAccessFlags(src); + barrier.dstAccessMask = calcAccessFlags(dst); + barrier.buffer = bufferImpl->m_buffer.m_buffer; + barrier.offset = 0; + barrier.size = bufferImpl->getDesc()->sizeInBytes; + + barriers.add(barrier); + } + + VkPipelineStageFlagBits srcStage = calcPipelineStageFlags(src, true); + VkPipelineStageFlagBits dstStage = calcPipelineStageFlags(dst, false); + + auto& vkApi = m_commandBuffer->m_renderer->m_api; + vkApi.vkCmdPipelineBarrier( + m_commandBuffer->m_commandBuffer, + srcStage, + dstStage, + 0, + 0, + nullptr, + (uint32_t)count, + barriers.getBuffer(), + 0, + nullptr); +} + +void ResourceCommandEncoder::endEncoding() +{ + // Insert memory barrier to ensure transfers are visible to the GPU. + auto& vkAPI = m_commandBuffer->m_renderer->m_api; + + VkMemoryBarrier memBarrier = {VK_STRUCTURE_TYPE_MEMORY_BARRIER}; + memBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + memBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + vkAPI.vkCmdPipelineBarrier( + m_commandBuffer->m_commandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + 0, + 1, + &memBarrier, + 0, + nullptr, + 0, + nullptr); +} + +void ResourceCommandEncoder::writeTimestamp(IQueryPool* queryPool, GfxIndex index) +{ + _writeTimestamp( + &m_commandBuffer->m_renderer->m_api, m_commandBuffer->m_commandBuffer, queryPool, index); +} + +void ResourceCommandEncoder::copyTexture( + ITextureResource* dst, + ResourceState dstState, + SubresourceRange dstSubresource, + ITextureResource::Offset3D dstOffset, + ITextureResource* src, + ResourceState srcState, + SubresourceRange srcSubresource, + ITextureResource::Offset3D srcOffset, + ITextureResource::Extents extent) +{ + auto srcImage = static_cast<TextureResourceImpl*>(src); + auto srcDesc = srcImage->getDesc(); + auto srcImageLayout = VulkanUtil::getImageLayoutFromState(srcState); + auto dstImage = static_cast<TextureResourceImpl*>(dst); + auto dstDesc = dstImage->getDesc(); + auto dstImageLayout = VulkanUtil::getImageLayoutFromState(dstState); + if (dstSubresource.layerCount == 0 && dstSubresource.mipLevelCount == 0) + { + extent = dstDesc->size; + dstSubresource.layerCount = dstDesc->arraySize; + if (dstSubresource.layerCount == 0) + dstSubresource.layerCount = 1; + dstSubresource.mipLevelCount = dstDesc->numMipLevels; + } + if (srcSubresource.layerCount == 0 && srcSubresource.mipLevelCount == 0) + { + extent = srcDesc->size; + srcSubresource.layerCount = srcDesc->arraySize; + if (srcSubresource.layerCount == 0) + srcSubresource.layerCount = 1; + srcSubresource.mipLevelCount = dstDesc->numMipLevels; + } + VkImageCopy region = {}; + region.srcSubresource.aspectMask = VulkanUtil::getAspectMask(srcSubresource.aspectMask, srcImage->m_vkformat); + region.srcSubresource.baseArrayLayer = srcSubresource.baseArrayLayer; + region.srcSubresource.mipLevel = srcSubresource.mipLevel; + region.srcSubresource.layerCount = srcSubresource.layerCount; + region.srcOffset = {(int32_t)srcOffset.x, (int32_t)srcOffset.y, (int32_t)srcOffset.z}; + region.dstSubresource.aspectMask = VulkanUtil::getAspectMask(dstSubresource.aspectMask, dstImage->m_vkformat); + region.dstSubresource.baseArrayLayer = dstSubresource.baseArrayLayer; + region.dstSubresource.mipLevel = dstSubresource.mipLevel; + region.dstSubresource.layerCount = dstSubresource.layerCount; + region.dstOffset = {(int32_t)dstOffset.x, (int32_t)dstOffset.y, (int32_t)dstOffset.z}; + region.extent = {(uint32_t)extent.width, (uint32_t)extent.height, (uint32_t)extent.depth}; + + auto& vkApi = m_commandBuffer->m_renderer->m_api; + vkApi.vkCmdCopyImage( + m_commandBuffer->m_commandBuffer, + srcImage->m_image, + srcImageLayout, + dstImage->m_image, + dstImageLayout, + 1, + ®ion); +} + +void ResourceCommandEncoder::uploadTextureData( + ITextureResource* dst, + SubresourceRange subResourceRange, + ITextureResource::Offset3D offset, + ITextureResource::Extents extend, + ITextureResource::SubresourceData* subResourceData, + GfxCount subResourceDataCount) +{ + // VALIDATION: dst must be in TransferDst state. + + auto& vkApi = m_commandBuffer->m_renderer->m_api; + auto dstImpl = static_cast<TextureResourceImpl*>(dst); + List<TextureResource::Extents> mipSizes; + + VkCommandBuffer commandBuffer = m_commandBuffer->m_commandBuffer; + auto& desc = *dstImpl->getDesc(); + // Calculate how large the buffer has to be + Size bufferSize = 0; + // Calculate how large an array entry is + for (GfxIndex j = subResourceRange.mipLevel; + j < subResourceRange.mipLevel + subResourceRange.mipLevelCount; + ++j) + { + const TextureResource::Extents mipSize = calcMipSize(desc.size, j); + + auto rowSizeInBytes = calcRowSize(desc.format, mipSize.width); + auto numRows = calcNumRows(desc.format, mipSize.height); + + mipSizes.add(mipSize); + + bufferSize += (rowSizeInBytes * numRows) * mipSize.depth; + } + + // Calculate the total size taking into account the array + bufferSize *= subResourceRange.layerCount; + + IBufferResource* uploadBuffer = nullptr; + Offset uploadBufferOffset = 0; + m_commandBuffer->m_transientHeap->allocateStagingBuffer( + bufferSize, uploadBuffer, uploadBufferOffset, MemoryType::Upload); + + // Copy into upload buffer + { + int subResourceCounter = 0; + + uint8_t* dstData; + uploadBuffer->map(nullptr, (void**)&dstData); + dstData += uploadBufferOffset; + uint8_t* dstDataStart; + dstDataStart = dstData; + + Offset dstSubresourceOffset = 0; + for (GfxIndex i = 0; i < subResourceRange.layerCount; ++i) + { + for (GfxIndex j = 0; j < (GfxCount)mipSizes.getCount(); ++j) + { + const auto& mipSize = mipSizes[j]; + + int subResourceIndex = subResourceCounter++; + auto initSubresource = subResourceData[subResourceIndex]; + + const ptrdiff_t srcRowStride = (ptrdiff_t)initSubresource.strideY; + const ptrdiff_t srcLayerStride = (ptrdiff_t)initSubresource.strideZ; + + auto dstRowSizeInBytes = calcRowSize(desc.format, mipSize.width); + auto numRows = calcNumRows(desc.format, mipSize.height); + auto dstLayerSizeInBytes = dstRowSizeInBytes * numRows; + + const uint8_t* srcLayer = (const uint8_t*)initSubresource.data; + uint8_t* dstLayer = dstData + dstSubresourceOffset; + + for (int k = 0; k < mipSize.depth; k++) + { + const uint8_t* srcRow = srcLayer; + uint8_t* dstRow = dstLayer; + + for (GfxCount l = 0; l < numRows; l++) + { + ::memcpy(dstRow, srcRow, dstRowSizeInBytes); + + dstRow += dstRowSizeInBytes; + srcRow += srcRowStride; + } + + dstLayer += dstLayerSizeInBytes; + srcLayer += srcLayerStride; + } + + dstSubresourceOffset += dstLayerSizeInBytes * mipSize.depth; + } + } + uploadBuffer->unmap(nullptr); + } + { + Offset srcOffset = uploadBufferOffset; + for (GfxIndex i = 0; i < subResourceRange.layerCount; ++i) + { + for (GfxIndex j = 0; j < (GfxCount)mipSizes.getCount(); ++j) + { + const auto& mipSize = mipSizes[j]; + + auto rowSizeInBytes = calcRowSize(desc.format, mipSize.width); + auto numRows = calcNumRows(desc.format, mipSize.height); + + // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkBufferImageCopy.html + // bufferRowLength and bufferImageHeight specify the data in buffer + // memory as a subregion of a larger two- or three-dimensional image, + // and control the addressing calculations of data in buffer memory. If + // either of these values is zero, that aspect of the buffer memory is + // considered to be tightly packed according to the imageExtent. + + VkBufferImageCopy region = {}; + + region.bufferOffset = srcOffset; + region.bufferRowLength = 0; // rowSizeInBytes; + region.bufferImageHeight = 0; + + region.imageSubresource.aspectMask = getAspectMaskFromFormat(dstImpl->m_vkformat); + region.imageSubresource.mipLevel = subResourceRange.mipLevel + uint32_t(j); + region.imageSubresource.baseArrayLayer = subResourceRange.baseArrayLayer + i; + region.imageSubresource.layerCount = 1; + region.imageOffset = {0, 0, 0}; + region.imageExtent = { + uint32_t(mipSize.width), uint32_t(mipSize.height), uint32_t(mipSize.depth)}; + + // Do the copy (do all depths in a single go) + vkApi.vkCmdCopyBufferToImage( + commandBuffer, + static_cast<BufferResourceImpl*>(uploadBuffer)->m_buffer.m_buffer, + dstImpl->m_image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + ®ion); + + // Next + srcOffset += rowSizeInBytes * numRows * mipSize.depth; + } + } + } +} + +void ResourceCommandEncoder::_clearColorImage( + TextureResourceViewImpl* viewImpl, ClearValue* clearValue) +{ + auto& api = m_commandBuffer->m_renderer->m_api; + auto layout = viewImpl->m_layout; + if (layout != VK_IMAGE_LAYOUT_GENERAL && layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + { + layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + m_commandBuffer->m_renderer->_transitionImageLayout( + m_commandBuffer->m_commandBuffer, + viewImpl->m_texture->m_image, + viewImpl->m_texture->m_vkformat, + *viewImpl->m_texture->getDesc(), + viewImpl->m_layout, + layout); + } + + VkImageSubresourceRange subresourceRange = {}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseArrayLayer = viewImpl->m_desc.subresourceRange.baseArrayLayer; + subresourceRange.baseMipLevel = viewImpl->m_desc.subresourceRange.mipLevel; + subresourceRange.layerCount = viewImpl->m_desc.subresourceRange.layerCount; + subresourceRange.levelCount = 1; + + VkClearColorValue vkClearColor = {}; + memcpy(vkClearColor.float32, clearValue->color.floatValues, sizeof(float) * 4); + + api.vkCmdClearColorImage( + m_commandBuffer->m_commandBuffer, + viewImpl->m_texture->m_image, + layout, + &vkClearColor, + 1, + &subresourceRange); + + if (layout != viewImpl->m_layout) + { + m_commandBuffer->m_renderer->_transitionImageLayout( + m_commandBuffer->m_commandBuffer, + viewImpl->m_texture->m_image, + viewImpl->m_texture->m_vkformat, + *viewImpl->m_texture->getDesc(), + layout, + viewImpl->m_layout); + } +} + +void ResourceCommandEncoder::_clearDepthImage( + TextureResourceViewImpl* viewImpl, ClearValue* clearValue, ClearResourceViewFlags::Enum flags) +{ + auto& api = m_commandBuffer->m_renderer->m_api; + auto layout = viewImpl->m_layout; + if (layout != VK_IMAGE_LAYOUT_GENERAL && layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + { + layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + m_commandBuffer->m_renderer->_transitionImageLayout( + m_commandBuffer->m_commandBuffer, + viewImpl->m_texture->m_image, + viewImpl->m_texture->m_vkformat, + *viewImpl->m_texture->getDesc(), + viewImpl->m_layout, + layout); + } + + VkImageSubresourceRange subresourceRange = {}; + if (flags & ClearResourceViewFlags::ClearDepth) + { + if (VulkanUtil::isDepthFormat(viewImpl->m_texture->m_vkformat)) + { + subresourceRange.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + } + } + if (flags & ClearResourceViewFlags::ClearStencil) + { + if (VulkanUtil::isStencilFormat(viewImpl->m_texture->m_vkformat)) + { + subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + } + subresourceRange.baseArrayLayer = viewImpl->m_desc.subresourceRange.baseArrayLayer; + subresourceRange.baseMipLevel = viewImpl->m_desc.subresourceRange.mipLevel; + subresourceRange.layerCount = viewImpl->m_desc.subresourceRange.layerCount; + subresourceRange.levelCount = 1; + + VkClearDepthStencilValue vkClearValue = {}; + vkClearValue.depth = clearValue->depthStencil.depth; + vkClearValue.stencil = clearValue->depthStencil.stencil; + + api.vkCmdClearDepthStencilImage( + m_commandBuffer->m_commandBuffer, + viewImpl->m_texture->m_image, + layout, + &vkClearValue, + 1, + &subresourceRange); + + if (layout != viewImpl->m_layout) + { + m_commandBuffer->m_renderer->_transitionImageLayout( + m_commandBuffer->m_commandBuffer, + viewImpl->m_texture->m_image, + viewImpl->m_texture->m_vkformat, + *viewImpl->m_texture->getDesc(), + layout, + viewImpl->m_layout); + } +} + +void ResourceCommandEncoder::_clearBuffer( + VkBuffer buffer, uint64_t bufferSize, const IResourceView::Desc& desc, uint32_t clearValue) +{ + auto& api = m_commandBuffer->m_renderer->m_api; + + FormatInfo info = {}; + gfxGetFormatInfo(desc.format, &info); + auto texelSize = info.blockSizeInBytes; + auto elementCount = desc.bufferRange.elementCount; + auto clearStart = (uint64_t)desc.bufferRange.firstElement * texelSize; + auto clearSize = bufferSize - clearStart; + if (elementCount != 0) + { + clearSize = (uint64_t)elementCount * texelSize; + } + api.vkCmdFillBuffer( + m_commandBuffer->m_commandBuffer, buffer, clearStart, clearSize, clearValue); +} + +void ResourceCommandEncoder::clearResourceView( + IResourceView* view, ClearValue* clearValue, ClearResourceViewFlags::Enum flags) +{ + auto& api = m_commandBuffer->m_renderer->m_api; + switch (view->getViewDesc()->type) + { + case IResourceView::Type::RenderTarget: + { + auto viewImpl = static_cast<TextureResourceViewImpl*>(view); + _clearColorImage(viewImpl, clearValue); + } + break; + case IResourceView::Type::DepthStencil: + { + auto viewImpl = static_cast<TextureResourceViewImpl*>(view); + _clearDepthImage(viewImpl, clearValue, flags); + } + break; + case IResourceView::Type::UnorderedAccess: + { + auto viewImplBase = static_cast<ResourceViewImpl*>(view); + switch (viewImplBase->m_type) + { + case ResourceViewImpl::ViewType::Texture: + { + auto viewImpl = static_cast<TextureResourceViewImpl*>(viewImplBase); + if ((flags & ClearResourceViewFlags::ClearDepth) || + (flags & ClearResourceViewFlags::ClearStencil)) + { + _clearDepthImage(viewImpl, clearValue, flags); + } + else + { + _clearColorImage(viewImpl, clearValue); + } + } + break; + case ResourceViewImpl::ViewType::PlainBuffer: + { + assert( + clearValue->color.uintValues[1] == clearValue->color.uintValues[0] && + clearValue->color.uintValues[2] == clearValue->color.uintValues[0] && + clearValue->color.uintValues[3] == clearValue->color.uintValues[0]); + auto viewImpl = static_cast<PlainBufferResourceViewImpl*>(viewImplBase); + uint64_t clearStart = viewImpl->m_desc.bufferRange.firstElement; + uint64_t clearSize = viewImpl->m_desc.bufferRange.elementCount; + + if (clearSize == 0) + clearSize = viewImpl->m_buffer->getDesc()->sizeInBytes - clearStart; + if (viewImpl->m_desc.bufferElementSize != 0) + clearSize *= viewImpl->m_desc.bufferElementSize; + api.vkCmdFillBuffer( + m_commandBuffer->m_commandBuffer, + viewImpl->m_buffer->m_buffer.m_buffer, + clearStart, + clearSize, + clearValue->color.uintValues[0]); + } + break; + case ResourceViewImpl::ViewType::TexelBuffer: + { + assert( + clearValue->color.uintValues[1] == clearValue->color.uintValues[0] && + clearValue->color.uintValues[2] == clearValue->color.uintValues[0] && + clearValue->color.uintValues[3] == clearValue->color.uintValues[0]); + auto viewImpl = static_cast<TexelBufferResourceViewImpl*>(viewImplBase); + _clearBuffer( + viewImpl->m_buffer->m_buffer.m_buffer, + viewImpl->m_buffer->getDesc()->sizeInBytes, + viewImpl->m_desc, + clearValue->color.uintValues[0]); + } + break; + } + } + break; + } +} + +void ResourceCommandEncoder::resolveResource( + ITextureResource* source, + ResourceState sourceState, + SubresourceRange sourceRange, + ITextureResource* dest, + ResourceState destState, + SubresourceRange destRange) +{ + auto srcTexture = static_cast<TextureResourceImpl*>(source); + auto srcExtent = srcTexture->getDesc()->size; + auto dstTexture = static_cast<TextureResourceImpl*>(dest); + + auto srcImage = srcTexture->m_image; + auto dstImage = dstTexture->m_image; + + auto srcImageLayout = VulkanUtil::getImageLayoutFromState(sourceState); + auto dstImageLayout = VulkanUtil::getImageLayoutFromState(destState); + + for (GfxIndex layer = 0; layer < sourceRange.layerCount; ++layer) + { + for (GfxIndex mip = 0; mip < sourceRange.mipLevelCount; ++mip) + { + VkImageResolve region = {}; + region.srcSubresource.aspectMask = VulkanUtil::getAspectMask(sourceRange.aspectMask, srcTexture->m_vkformat); + region.srcSubresource.baseArrayLayer = layer + sourceRange.baseArrayLayer; + region.srcSubresource.layerCount = 1; + region.srcSubresource.mipLevel = mip + sourceRange.mipLevel; + region.srcOffset = {0, 0, 0}; + region.dstSubresource.aspectMask = VulkanUtil::getAspectMask(destRange.aspectMask, dstTexture->m_vkformat); + region.dstSubresource.baseArrayLayer = layer + destRange.baseArrayLayer; + region.dstSubresource.layerCount = 1; + region.dstSubresource.mipLevel = mip + destRange.mipLevel; + region.dstOffset = {0, 0, 0}; + region.extent = { + (uint32_t)srcExtent.width, (uint32_t)srcExtent.height, (uint32_t)srcExtent.depth}; + + auto& vkApi = m_commandBuffer->m_renderer->m_api; + vkApi.vkCmdResolveImage( + m_commandBuffer->m_commandBuffer, + srcImage, + srcImageLayout, + dstImage, + dstImageLayout, + 1, + ®ion); + } + } +} + +void ResourceCommandEncoder::resolveQuery( + IQueryPool* queryPool, GfxIndex index, GfxCount count, IBufferResource* buffer, Offset offset) +{ + auto& vkApi = m_commandBuffer->m_renderer->m_api; + auto poolImpl = static_cast<QueryPoolImpl*>(queryPool); + auto bufferImpl = static_cast<BufferResourceImpl*>(buffer); + vkApi.vkCmdCopyQueryPoolResults( + m_commandBuffer->m_commandBuffer, + poolImpl->m_pool, + index, + count, + bufferImpl->m_buffer.m_buffer, + offset, + sizeof(uint64_t), + VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); +} + +void ResourceCommandEncoder::copyTextureToBuffer( + IBufferResource* dst, + Offset dstOffset, + Size dstSize, + Size dstRowStride, + ITextureResource* src, + ResourceState srcState, + SubresourceRange srcSubresource, + ITextureResource::Offset3D srcOffset, + ITextureResource::Extents extent) +{ + assert(srcSubresource.mipLevelCount <= 1); + + auto image = static_cast<TextureResourceImpl*>(src); + auto desc = image->getDesc(); + auto buffer = static_cast<BufferResourceImpl*>(dst); + auto srcImageLayout = VulkanUtil::getImageLayoutFromState(srcState); + + VkBufferImageCopy region = {}; + region.bufferOffset = dstOffset; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.aspectMask = VulkanUtil::getAspectMask(srcSubresource.aspectMask, image->m_vkformat); + region.imageSubresource.mipLevel = srcSubresource.mipLevel; + region.imageSubresource.baseArrayLayer = srcSubresource.baseArrayLayer; + region.imageSubresource.layerCount = srcSubresource.layerCount; + region.imageOffset = {(int32_t)srcOffset.x, (int32_t)srcOffset.y, (int32_t)srcOffset.z}; + region.imageExtent = {uint32_t(extent.width), uint32_t(extent.height), uint32_t(extent.depth)}; + + auto& vkApi = m_commandBuffer->m_renderer->m_api; + vkApi.vkCmdCopyImageToBuffer( + m_commandBuffer->m_commandBuffer, + image->m_image, + srcImageLayout, + buffer->m_buffer.m_buffer, + 1, + ®ion); +} + +void ResourceCommandEncoder::textureSubresourceBarrier( + ITextureResource* texture, + SubresourceRange subresourceRange, + ResourceState src, + ResourceState dst) +{ + ShortList<VkImageMemoryBarrier> barriers; + auto image = static_cast<TextureResourceImpl*>(texture); + auto desc = image->getDesc(); + + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.image = image->m_image; + barrier.oldLayout = translateImageLayout(src); + barrier.newLayout = translateImageLayout(dst); + barrier.subresourceRange.aspectMask = VulkanUtil::getAspectMask(subresourceRange.aspectMask, image->m_vkformat); + barrier.subresourceRange.baseArrayLayer = subresourceRange.baseArrayLayer; + barrier.subresourceRange.baseMipLevel = subresourceRange.mipLevel; + barrier.subresourceRange.layerCount = subresourceRange.layerCount; + barrier.subresourceRange.levelCount = subresourceRange.mipLevelCount; + barrier.srcAccessMask = calcAccessFlags(src); + barrier.dstAccessMask = calcAccessFlags(dst); + barriers.add(barrier); + + VkPipelineStageFlagBits srcStage = calcPipelineStageFlags(src, true); + VkPipelineStageFlagBits dstStage = calcPipelineStageFlags(dst, false); + + auto& vkApi = m_commandBuffer->m_renderer->m_api; + vkApi.vkCmdPipelineBarrier( + m_commandBuffer->m_commandBuffer, + srcStage, + dstStage, + 0, + 0, + nullptr, + 0, + nullptr, + (uint32_t)barriers.getCount(), + barriers.getArrayView().getBuffer()); +} + +void ResourceCommandEncoder::beginDebugEvent(const char* name, float rgbColor[3]) +{ + auto& vkApi = m_commandBuffer->m_renderer->m_api; + if (vkApi.vkCmdDebugMarkerBeginEXT) + { + VkDebugMarkerMarkerInfoEXT eventInfo = {}; + eventInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; + eventInfo.pMarkerName = name; + eventInfo.color[0] = rgbColor[0]; + eventInfo.color[1] = rgbColor[1]; + eventInfo.color[2] = rgbColor[2]; + eventInfo.color[3] = 1.0f; + vkApi.vkCmdDebugMarkerBeginEXT(m_commandBuffer->m_commandBuffer, &eventInfo); + } +} + +void ResourceCommandEncoder::endDebugEvent() +{ + auto& vkApi = m_commandBuffer->m_renderer->m_api; + if (vkApi.vkCmdDebugMarkerEndEXT) + { + vkApi.vkCmdDebugMarkerEndEXT(m_commandBuffer->m_commandBuffer); + } +} + +void RenderCommandEncoder::beginPass(IRenderPassLayout* renderPass, IFramebuffer* framebuffer) +{ + FramebufferImpl* framebufferImpl = static_cast<FramebufferImpl*>(framebuffer); + if (!framebuffer) + framebufferImpl = this->m_device->m_emptyFramebuffer; + RenderPassLayoutImpl* renderPassImpl = static_cast<RenderPassLayoutImpl*>(renderPass); + VkClearValue clearValues[kMaxTargets] = {}; + VkRenderPassBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + beginInfo.framebuffer = framebufferImpl->m_handle; + beginInfo.renderPass = renderPassImpl->m_renderPass; + uint32_t targetCount = (uint32_t)framebufferImpl->renderTargetViews.getCount(); + if (framebufferImpl->depthStencilView) + targetCount++; + beginInfo.clearValueCount = targetCount; + beginInfo.renderArea.extent.width = framebufferImpl->m_width; + beginInfo.renderArea.extent.height = framebufferImpl->m_height; + beginInfo.pClearValues = framebufferImpl->m_clearValues; + auto& api = *m_api; + api.vkCmdBeginRenderPass(m_vkCommandBuffer, &beginInfo, VK_SUBPASS_CONTENTS_INLINE); +} + +void RenderCommandEncoder::endEncoding() +{ + auto& api = *m_api; + api.vkCmdEndRenderPass(m_vkCommandBuffer); + endEncodingImpl(); +} + +Result RenderCommandEncoder::bindPipeline( + IPipelineState* pipelineState, IShaderObject** outRootObject) +{ + return setPipelineStateImpl(pipelineState, outRootObject); +} + +Result RenderCommandEncoder::bindPipelineWithRootObject( + IPipelineState* pipelineState, IShaderObject* rootObject) +{ + return setPipelineStateWithRootObjectImpl(pipelineState, rootObject); +} + +void RenderCommandEncoder::setViewports(GfxCount count, const Viewport* viewports) +{ + static const int kMaxViewports = 8; // TODO: base on device caps + assert(count <= kMaxViewports); + + m_viewports.setCount(count); + for (GfxIndex ii = 0; ii < count; ++ii) + { + auto& inViewport = viewports[ii]; + auto& vkViewport = m_viewports[ii]; + + vkViewport.x = inViewport.originX; + vkViewport.y = inViewport.originY + inViewport.extentY; + vkViewport.width = inViewport.extentX; + vkViewport.height = -inViewport.extentY; + vkViewport.minDepth = inViewport.minZ; + vkViewport.maxDepth = inViewport.maxZ; + } + + auto& api = *m_api; + api.vkCmdSetViewport(m_vkCommandBuffer, 0, uint32_t(count), m_viewports.getBuffer()); +} + +void RenderCommandEncoder::setScissorRects(GfxCount count, const ScissorRect* rects) +{ + static const int kMaxScissorRects = 8; // TODO: base on device caps + assert(count <= kMaxScissorRects); + + m_scissorRects.setCount(count); + for (GfxIndex ii = 0; ii < count; ++ii) + { + auto& inRect = rects[ii]; + auto& vkRect = m_scissorRects[ii]; + + vkRect.offset.x = int32_t(inRect.minX); + vkRect.offset.y = int32_t(inRect.minY); + vkRect.extent.width = uint32_t(inRect.maxX - inRect.minX); + vkRect.extent.height = uint32_t(inRect.maxY - inRect.minY); + } + + auto& api = *m_api; + api.vkCmdSetScissor(m_vkCommandBuffer, 0, uint32_t(count), m_scissorRects.getBuffer()); +} + +void RenderCommandEncoder::setPrimitiveTopology(PrimitiveTopology topology) +{ + auto& api = *m_api; + if (api.vkCmdSetPrimitiveTopologyEXT) + { + api.vkCmdSetPrimitiveTopologyEXT( + m_vkCommandBuffer, VulkanUtil::getVkPrimitiveTopology(topology)); + } + else + { + switch (topology) + { + case PrimitiveTopology::TriangleList: + break; + default: + // We are using a non-list topology, but we don't have dynmaic state + // extension, error out. + assert(!"Non-list topology requires VK_EXT_extended_dynamic_states, which " + "is not present."); + break; + } + } +} + +void RenderCommandEncoder::setVertexBuffers( + GfxIndex startSlot, + GfxCount slotCount, + IBufferResource* const* buffers, + const Offset* offsets) +{ + for (GfxIndex i = 0; i < GfxIndex(slotCount); i++) + { + GfxIndex slotIndex = startSlot + i; + BufferResourceImpl* buffer = static_cast<BufferResourceImpl*>(buffers[i]); + if (buffer) + { + VkBuffer vertexBuffers[] = {buffer->m_buffer.m_buffer}; + VkDeviceSize offset = VkDeviceSize(offsets[i]); + + m_api->vkCmdBindVertexBuffers( + m_vkCommandBuffer, (uint32_t)slotIndex, 1, vertexBuffers, &offset); + } + } +} + +void RenderCommandEncoder::setIndexBuffer( + IBufferResource* buffer, Format indexFormat, Offset offset) +{ + VkIndexType indexType = VK_INDEX_TYPE_UINT16; + switch (indexFormat) + { + case Format::R16_UINT: + indexType = VK_INDEX_TYPE_UINT16; + break; + case Format::R32_UINT: + indexType = VK_INDEX_TYPE_UINT32; + break; + default: + assert(!"unsupported index format"); + } + + BufferResourceImpl* bufferImpl = static_cast<BufferResourceImpl*>(buffer); + + m_api->vkCmdBindIndexBuffer( + m_vkCommandBuffer, bufferImpl->m_buffer.m_buffer, (VkDeviceSize)offset, indexType); +} + +void RenderCommandEncoder::prepareDraw() +{ + auto pipeline = static_cast<PipelineStateImpl*>(m_currentPipeline.Ptr()); + if (!pipeline) + { + assert(!"Invalid render pipeline"); + return; + } + bindRenderState(VK_PIPELINE_BIND_POINT_GRAPHICS); +} + +void RenderCommandEncoder::draw(GfxCount vertexCount, GfxIndex startVertex) +{ + prepareDraw(); + auto& api = *m_api; + api.vkCmdDraw(m_vkCommandBuffer, vertexCount, 1, 0, 0); +} + +void RenderCommandEncoder::drawIndexed( + GfxCount indexCount, GfxIndex startIndex, GfxIndex baseVertex) +{ + prepareDraw(); + auto& api = *m_api; + api.vkCmdDrawIndexed(m_vkCommandBuffer, indexCount, 1, startIndex, baseVertex, 0); +} + +void RenderCommandEncoder::setStencilReference(uint32_t referenceValue) +{ + auto& api = *m_api; + api.vkCmdSetStencilReference(m_vkCommandBuffer, VK_STENCIL_FRONT_AND_BACK, referenceValue); +} + +void RenderCommandEncoder::drawIndirect( + GfxCount maxDrawCount, + IBufferResource* argBuffer, + Offset argOffset, + IBufferResource* countBuffer, + Offset countOffset) +{ + // Vulkan does not support sourcing the count from a buffer. + assert(!countBuffer); + + prepareDraw(); + auto& api = *m_api; + auto argBufferImpl = static_cast<BufferResourceImpl*>(argBuffer); + api.vkCmdDrawIndirect( + m_vkCommandBuffer, + argBufferImpl->m_buffer.m_buffer, + argOffset, + maxDrawCount, + sizeof(VkDrawIndirectCommand)); +} + +void RenderCommandEncoder::drawIndexedIndirect( + GfxCount maxDrawCount, + IBufferResource* argBuffer, + Offset argOffset, + IBufferResource* countBuffer, + Offset countOffset) +{ + // Vulkan does not support sourcing the count from a buffer. + assert(!countBuffer); + + prepareDraw(); + auto& api = *m_api; + auto argBufferImpl = static_cast<BufferResourceImpl*>(argBuffer); + api.vkCmdDrawIndexedIndirect( + m_vkCommandBuffer, + argBufferImpl->m_buffer.m_buffer, + argOffset, + maxDrawCount, + sizeof(VkDrawIndexedIndirectCommand)); +} + +Result RenderCommandEncoder::setSamplePositions( + GfxCount samplesPerPixel, GfxCount pixelCount, const SamplePosition* samplePositions) +{ + if (m_api->vkCmdSetSampleLocationsEXT) + { + VkSampleLocationsInfoEXT sampleLocInfo = {}; + sampleLocInfo.sType = VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT; + sampleLocInfo.sampleLocationsCount = samplesPerPixel * pixelCount; + sampleLocInfo.sampleLocationsPerPixel = (VkSampleCountFlagBits)samplesPerPixel; + m_api->vkCmdSetSampleLocationsEXT(m_vkCommandBuffer, &sampleLocInfo); + return SLANG_OK; + } + return SLANG_E_NOT_AVAILABLE; +} + +void RenderCommandEncoder::drawInstanced( + GfxCount vertexCount, + GfxCount instanceCount, + GfxIndex startVertex, + GfxIndex startInstanceLocation) +{ + prepareDraw(); + auto& api = *m_api; + api.vkCmdDraw( + m_vkCommandBuffer, vertexCount, instanceCount, startVertex, startInstanceLocation); +} + +void RenderCommandEncoder::drawIndexedInstanced( + GfxCount indexCount, + GfxCount instanceCount, + GfxIndex startIndexLocation, + GfxIndex baseVertexLocation, + GfxIndex startInstanceLocation) +{ + prepareDraw(); + auto& api = *m_api; + api.vkCmdDrawIndexed( + m_vkCommandBuffer, + indexCount, + instanceCount, + startIndexLocation, + baseVertexLocation, + startInstanceLocation); +} + +void ComputeCommandEncoder::endEncoding() { endEncodingImpl(); } + +Result ComputeCommandEncoder::bindPipeline( + IPipelineState* pipelineState, IShaderObject** outRootObject) +{ + return setPipelineStateImpl(pipelineState, outRootObject); +} + +Result ComputeCommandEncoder::bindPipelineWithRootObject( + IPipelineState* pipelineState, IShaderObject* rootObject) +{ + return setPipelineStateWithRootObjectImpl(pipelineState, rootObject); +} + +void ComputeCommandEncoder::dispatchCompute(int x, int y, int z) +{ + auto pipeline = static_cast<PipelineStateImpl*>(m_currentPipeline.Ptr()); + if (!pipeline) + { + assert(!"Invalid compute pipeline"); + return; + } + + // Also create descriptor sets based on the given pipeline layout + bindRenderState(VK_PIPELINE_BIND_POINT_COMPUTE); + m_api->vkCmdDispatch(m_vkCommandBuffer, x, y, z); +} + +void ComputeCommandEncoder::dispatchComputeIndirect(IBufferResource* argBuffer, Offset offset) +{ + SLANG_UNIMPLEMENTED_X("dispatchComputeIndirect"); +} + +void RayTracingCommandEncoder::_memoryBarrier( + int count, + IAccelerationStructure* const* structures, + AccessFlag srcAccess, + AccessFlag destAccess) +{ + ShortList<VkBufferMemoryBarrier> memBarriers; + memBarriers.setCount(count); + for (int i = 0; i < count; i++) + { + memBarriers[i].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + memBarriers[i].pNext = nullptr; + memBarriers[i].dstAccessMask = translateAccelerationStructureAccessFlag(destAccess); + memBarriers[i].srcAccessMask = translateAccelerationStructureAccessFlag(srcAccess); + memBarriers[i].srcQueueFamilyIndex = m_commandBuffer->m_renderer->m_queueFamilyIndex; + memBarriers[i].dstQueueFamilyIndex = m_commandBuffer->m_renderer->m_queueFamilyIndex; + + auto asImpl = static_cast<AccelerationStructureImpl*>(structures[i]); + memBarriers[i].buffer = asImpl->m_buffer->m_buffer.m_buffer; + memBarriers[i].offset = asImpl->m_offset; + memBarriers[i].size = asImpl->m_size; + } + m_commandBuffer->m_renderer->m_api.vkCmdPipelineBarrier( + m_commandBuffer->m_commandBuffer, + VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | + VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, + 0, + 0, + nullptr, + (uint32_t)memBarriers.getCount(), + memBarriers.getArrayView().getBuffer(), + 0, + nullptr); +} + +void RayTracingCommandEncoder::_queryAccelerationStructureProperties( + GfxCount accelerationStructureCount, + IAccelerationStructure* const* accelerationStructures, + GfxCount queryCount, + AccelerationStructureQueryDesc* queryDescs) +{ + ShortList<VkAccelerationStructureKHR> vkHandles; + vkHandles.setCount(accelerationStructureCount); + for (GfxIndex i = 0; i < accelerationStructureCount; i++) + { + vkHandles[i] = + static_cast<AccelerationStructureImpl*>(accelerationStructures[i])->m_vkHandle; + } + auto vkHandlesView = vkHandles.getArrayView(); + for (GfxIndex i = 0; i < queryCount; i++) + { + VkQueryType queryType; + switch (queryDescs[i].queryType) + { + case QueryType::AccelerationStructureCompactedSize: + queryType = VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR; + break; + case QueryType::AccelerationStructureSerializedSize: + queryType = VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SERIALIZATION_SIZE_KHR; + break; + case QueryType::AccelerationStructureCurrentSize: + continue; + default: + getDebugCallback()->handleMessage( + DebugMessageType::Error, + DebugMessageSource::Layer, + "Invalid query type for use in queryAccelerationStructureProperties."); + return; + } + auto queryPool = static_cast<QueryPoolImpl*>(queryDescs[i].queryPool)->m_pool; + m_commandBuffer->m_renderer->m_api.vkCmdResetQueryPool( + m_commandBuffer->m_commandBuffer, + queryPool, + (uint32_t)queryDescs[i].firstQueryIndex, + 1); + m_commandBuffer->m_renderer->m_api.vkCmdWriteAccelerationStructuresPropertiesKHR( + m_commandBuffer->m_commandBuffer, + accelerationStructureCount, + vkHandlesView.getBuffer(), + queryType, + queryPool, + queryDescs[i].firstQueryIndex); + } +} + +void RayTracingCommandEncoder::buildAccelerationStructure( + const IAccelerationStructure::BuildDesc& desc, + GfxCount propertyQueryCount, + AccelerationStructureQueryDesc* queryDescs) +{ + AccelerationStructureBuildGeometryInfoBuilder geomInfoBuilder; + if (geomInfoBuilder.build(desc.inputs, getDebugCallback()) != SLANG_OK) + return; + + if (desc.dest) + { + geomInfoBuilder.buildInfo.dstAccelerationStructure = + static_cast<AccelerationStructureImpl*>(desc.dest)->m_vkHandle; + } + if (desc.source) + { + geomInfoBuilder.buildInfo.srcAccelerationStructure = + static_cast<AccelerationStructureImpl*>(desc.source)->m_vkHandle; + } + geomInfoBuilder.buildInfo.scratchData.deviceAddress = desc.scratchData; + + List<VkAccelerationStructureBuildRangeInfoKHR> rangeInfos; + rangeInfos.setCount(geomInfoBuilder.primitiveCounts.getCount()); + for (Index i = 0; i < geomInfoBuilder.primitiveCounts.getCount(); i++) + { + auto& rangeInfo = rangeInfos[i]; + rangeInfo.primitiveCount = geomInfoBuilder.primitiveCounts[i]; + rangeInfo.firstVertex = 0; + rangeInfo.primitiveOffset = 0; + rangeInfo.transformOffset = 0; + } + + auto rangeInfoPtr = rangeInfos.getBuffer(); + m_commandBuffer->m_renderer->m_api.vkCmdBuildAccelerationStructuresKHR( + m_commandBuffer->m_commandBuffer, 1, &geomInfoBuilder.buildInfo, &rangeInfoPtr); + + if (propertyQueryCount) + { + _memoryBarrier(1, &desc.dest, AccessFlag::Write, AccessFlag::Read); + _queryAccelerationStructureProperties(1, &desc.dest, propertyQueryCount, queryDescs); + } +} + +void RayTracingCommandEncoder::copyAccelerationStructure( + IAccelerationStructure* dest, IAccelerationStructure* src, AccelerationStructureCopyMode mode) +{ + VkCopyAccelerationStructureInfoKHR copyInfo = { + VK_STRUCTURE_TYPE_COPY_ACCELERATION_STRUCTURE_INFO_KHR}; + copyInfo.src = static_cast<AccelerationStructureImpl*>(src)->m_vkHandle; + copyInfo.dst = static_cast<AccelerationStructureImpl*>(dest)->m_vkHandle; + switch (mode) + { + case AccelerationStructureCopyMode::Clone: + copyInfo.mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_KHR; + break; + case AccelerationStructureCopyMode::Compact: + copyInfo.mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_KHR; + break; + default: + getDebugCallback()->handleMessage( + DebugMessageType::Error, + DebugMessageSource::Layer, + "Unsupported AccelerationStructureCopyMode."); + return; + } + m_commandBuffer->m_renderer->m_api.vkCmdCopyAccelerationStructureKHR( + m_commandBuffer->m_commandBuffer, ©Info); +} + +void RayTracingCommandEncoder::queryAccelerationStructureProperties( + GfxCount accelerationStructureCount, + IAccelerationStructure* const* accelerationStructures, + GfxCount queryCount, + AccelerationStructureQueryDesc* queryDescs) +{ + _queryAccelerationStructureProperties( + accelerationStructureCount, accelerationStructures, queryCount, queryDescs); +} + +void RayTracingCommandEncoder::serializeAccelerationStructure( + DeviceAddress dest, IAccelerationStructure* source) +{ + VkCopyAccelerationStructureToMemoryInfoKHR copyInfo = { + VK_STRUCTURE_TYPE_COPY_ACCELERATION_STRUCTURE_TO_MEMORY_INFO_KHR}; + copyInfo.src = static_cast<AccelerationStructureImpl*>(source)->m_vkHandle; + copyInfo.dst.deviceAddress = dest; + copyInfo.mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_SERIALIZE_KHR; + m_commandBuffer->m_renderer->m_api.vkCmdCopyAccelerationStructureToMemoryKHR( + m_commandBuffer->m_commandBuffer, ©Info); +} + +void RayTracingCommandEncoder::deserializeAccelerationStructure( + IAccelerationStructure* dest, DeviceAddress source) +{ + VkCopyMemoryToAccelerationStructureInfoKHR copyInfo = { + VK_STRUCTURE_TYPE_COPY_MEMORY_TO_ACCELERATION_STRUCTURE_INFO_KHR}; + copyInfo.src.deviceAddress = source; + copyInfo.dst = static_cast<AccelerationStructureImpl*>(dest)->m_vkHandle; + copyInfo.mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_DESERIALIZE_KHR; + m_commandBuffer->m_renderer->m_api.vkCmdCopyMemoryToAccelerationStructureKHR( + m_commandBuffer->m_commandBuffer, ©Info); +} + +void RayTracingCommandEncoder::bindPipeline(IPipelineState* pipeline, IShaderObject** outRootObject) +{ + setPipelineStateImpl(pipeline, outRootObject); +} + +Result RayTracingCommandEncoder::bindPipelineWithRootObject( + IPipelineState* pipelineState, IShaderObject* rootObject) +{ + return setPipelineStateWithRootObjectImpl(pipelineState, rootObject); +} + +void RayTracingCommandEncoder::dispatchRays( + GfxIndex raygenShaderIndex, + IShaderTable* shaderTable, + GfxCount width, + GfxCount height, + GfxCount depth) +{ + auto vkApi = m_commandBuffer->m_renderer->m_api; + auto vkCommandBuffer = m_commandBuffer->m_commandBuffer; + + bindRenderState(VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR); + + auto rtProps = vkApi.m_rtProperties; + auto shaderTableImpl = (ShaderTableImpl*)shaderTable; + auto alignedHandleSize = + VulkanUtil::calcAligned(rtProps.shaderGroupHandleSize, rtProps.shaderGroupHandleAlignment); + + auto shaderTableBuffer = shaderTableImpl->getOrCreateBuffer( + m_currentPipeline, + m_commandBuffer->m_transientHeap, + static_cast<ResourceCommandEncoder*>(this)); + + VkStridedDeviceAddressRegionKHR raygenSBT; + raygenSBT.deviceAddress = shaderTableBuffer->getDeviceAddress(); + raygenSBT.stride = VulkanUtil::calcAligned(alignedHandleSize, rtProps.shaderGroupBaseAlignment); + raygenSBT.size = raygenSBT.stride; + + VkStridedDeviceAddressRegionKHR missSBT; + missSBT.deviceAddress = raygenSBT.deviceAddress + raygenSBT.size; + missSBT.stride = alignedHandleSize; + missSBT.size = shaderTableImpl->m_missTableSize; + + VkStridedDeviceAddressRegionKHR hitSBT; + hitSBT.deviceAddress = missSBT.deviceAddress + missSBT.size; + hitSBT.stride = alignedHandleSize; + hitSBT.size = shaderTableImpl->m_hitTableSize; + + // TODO: Are callable shaders needed? + VkStridedDeviceAddressRegionKHR callableSBT; + callableSBT.deviceAddress = 0; + callableSBT.stride = 0; + callableSBT.size = 0; + + vkApi.vkCmdTraceRaysKHR( + vkCommandBuffer, + &raygenSBT, + &missSBT, + &hitSBT, + &callableSBT, + (uint32_t)width, + (uint32_t)height, + (uint32_t)depth); +} + +void RayTracingCommandEncoder::endEncoding() { endEncodingImpl(); } + +} // namespace vk +} // namespace gfx |
