#include #include "dxvk_device.h" #include "dxvk_context.h" #include "dxvk_main.h" namespace dxvk { DxvkContext::DxvkContext(const Rc& device) : m_device(device) { } DxvkContext::~DxvkContext() { } void DxvkContext::beginRecording(const Rc& cmdList) { m_cmd = cmdList; m_cmd->beginRecording(); // The current state of the internal command buffer is // undefined, so we have to bind and set up everything // before any draw or dispatch command is recorded. m_flags.clr( DxvkContextFlag::GpRenderPassBound); m_flags.set( DxvkContextFlag::GpDirtyPipeline, DxvkContextFlag::GpDirtyPipelineState, DxvkContextFlag::GpDirtyDynamicState, DxvkContextFlag::GpDirtyResources, DxvkContextFlag::GpDirtyVertexBuffers, DxvkContextFlag::GpDirtyIndexBuffer, DxvkContextFlag::CpDirtyPipeline, DxvkContextFlag::CpDirtyPipelineState, DxvkContextFlag::CpDirtyResources); } Rc DxvkContext::endRecording() { this->renderPassEnd(); m_cmd->endRecording(); return std::exchange(m_cmd, nullptr); } void DxvkContext::bindFramebuffer(const Rc& fb) { if (m_state.om.framebuffer != fb) { this->renderPassEnd(); m_state.om.framebuffer = fb; if (fb != nullptr) { m_state.gp.state.msSampleCount = fb->sampleCount(); m_state.gp.state.omRenderPass = fb->renderPass(); m_flags.set(DxvkContextFlag::GpDirtyPipelineState); } } } void DxvkContext::bindIndexBuffer( const DxvkBufferSlice& buffer, VkIndexType indexType) { if (!m_state.vi.indexBuffer.matches(buffer) || (m_state.vi.indexType != indexType)) { m_state.vi.indexBuffer = buffer; m_state.vi.indexType = indexType; m_flags.set(DxvkContextFlag::GpDirtyIndexBuffer); } } void DxvkContext::bindResourceBuffer( uint32_t slot, const DxvkBufferSlice& buffer) { if (!m_rc[slot].bufferSlice.matches(buffer)) { m_rc[slot].bufferSlice = buffer; m_flags.set( DxvkContextFlag::CpDirtyResources, DxvkContextFlag::GpDirtyResources); } } void DxvkContext::bindResourceTexelBuffer( uint32_t slot, const Rc& bufferView) { if (m_rc[slot].bufferView != bufferView) { m_rc[slot].bufferView = bufferView; m_flags.set( DxvkContextFlag::CpDirtyResources, DxvkContextFlag::GpDirtyResources); } } void DxvkContext::bindResourceImage( uint32_t slot, const Rc& image) { if (m_rc[slot].imageView != image) { m_rc[slot].imageView = image; m_flags.set( DxvkContextFlag::CpDirtyResources, DxvkContextFlag::GpDirtyResources); } } void DxvkContext::bindResourceSampler( uint32_t slot, const Rc& sampler) { if (m_rc[slot].sampler != sampler) { m_rc[slot].sampler = sampler; m_flags.set( DxvkContextFlag::CpDirtyResources, DxvkContextFlag::GpDirtyResources); } } void DxvkContext::bindShader( VkShaderStageFlagBits stage, const Rc& shader) { DxvkShaderStage* shaderStage = nullptr; switch (stage) { case VK_SHADER_STAGE_VERTEX_BIT: shaderStage = &m_state.gp.vs; break; case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: shaderStage = &m_state.gp.tcs; break; case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: shaderStage = &m_state.gp.tes; break; case VK_SHADER_STAGE_GEOMETRY_BIT: shaderStage = &m_state.gp.gs; break; case VK_SHADER_STAGE_FRAGMENT_BIT: shaderStage = &m_state.gp.fs; break; case VK_SHADER_STAGE_COMPUTE_BIT: shaderStage = &m_state.cp.cs; break; default: return; } if (shaderStage->shader != shader) { shaderStage->shader = shader; if (stage == VK_SHADER_STAGE_COMPUTE_BIT) { m_flags.set( DxvkContextFlag::CpDirtyPipeline, DxvkContextFlag::CpDirtyPipelineState, DxvkContextFlag::CpDirtyResources); } else { m_flags.set( DxvkContextFlag::GpDirtyPipeline, DxvkContextFlag::GpDirtyPipelineState, DxvkContextFlag::GpDirtyResources); } } } void DxvkContext::bindVertexBuffer( uint32_t binding, const DxvkBufferSlice& buffer, uint32_t stride) { if (!m_state.vi.vertexBuffers[binding].matches(buffer)) { m_state.vi.vertexBuffers[binding] = buffer; m_flags.set(DxvkContextFlag::GpDirtyVertexBuffers); } if (m_state.vi.vertexStrides[binding] != stride) { m_state.vi.vertexStrides[binding] = stride; m_flags.set(DxvkContextFlag::GpDirtyPipelineState); } } void DxvkContext::clearColorImage( const Rc& image, const VkClearColorValue& value, const VkImageSubresourceRange& subresources) { this->renderPassEnd(); m_barriers.accessImage(image, subresources, VK_IMAGE_LAYOUT_UNDEFINED, image->info().stages, image->info().access, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); m_barriers.recordCommands(m_cmd); m_cmd->cmdClearColorImage(image->handle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &value, 1, &subresources); m_barriers.accessImage(image, subresources, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, image->info().layout, image->info().stages, image->info().access); m_barriers.recordCommands(m_cmd); m_cmd->trackResource(image); } void DxvkContext::clearDepthStencilImage( const Rc& image, const VkClearDepthStencilValue& value, const VkImageSubresourceRange& subresources) { this->renderPassEnd(); m_barriers.accessImage(image, subresources, VK_IMAGE_LAYOUT_UNDEFINED, image->info().stages, image->info().access, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); m_barriers.recordCommands(m_cmd); m_cmd->cmdClearDepthStencilImage(image->handle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &value, 1, &subresources); m_barriers.accessImage(image, subresources, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, image->info().layout, image->info().stages, image->info().access); m_barriers.recordCommands(m_cmd); m_cmd->trackResource(image); } void DxvkContext::clearRenderTarget( const VkClearAttachment& attachment, const VkClearRect& clearArea) { // We only need the framebuffer to be bound. Flushing the // entire pipeline state is not required and might actually // cause problems if the current pipeline state is invalid. this->renderPassBegin(); m_cmd->cmdClearAttachments( 1, &attachment, 1, &clearArea); } void DxvkContext::copyBuffer( const Rc& dstBuffer, VkDeviceSize dstOffset, const Rc& srcBuffer, VkDeviceSize srcOffset, VkDeviceSize numBytes) { if (numBytes == 0) return; this->renderPassEnd(); auto dstSlice = dstBuffer->subSlice(dstOffset, numBytes); auto srcSlice = srcBuffer->subSlice(srcOffset, numBytes); VkBufferCopy bufferRegion; bufferRegion.srcOffset = srcSlice.offset(); bufferRegion.dstOffset = dstSlice.offset(); bufferRegion.size = dstSlice.length(); m_cmd->cmdCopyBuffer( srcSlice.handle(), dstSlice.handle(), 1, &bufferRegion); m_barriers.accessBuffer(srcSlice, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, srcBuffer->info().stages, srcBuffer->info().access); m_barriers.accessBuffer(dstSlice, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, dstBuffer->info().stages, dstBuffer->info().access); m_barriers.recordCommands(m_cmd); m_cmd->trackResource(dstBuffer->resource()); m_cmd->trackResource(srcBuffer->resource()); } void DxvkContext::copyBufferToImage( const Rc& dstImage, VkImageSubresourceLayers dstSubresource, VkOffset3D dstOffset, VkExtent3D dstExtent, const Rc& srcBuffer, VkDeviceSize srcOffset, VkExtent2D srcExtent) { this->renderPassEnd(); auto srcSlice = srcBuffer->subSlice(srcOffset, 0); const VkImageSubresourceRange dstSubresourceRange = { dstSubresource.aspectMask, dstSubresource.mipLevel, 1, dstSubresource.baseArrayLayer, dstSubresource.layerCount }; m_barriers.accessImage( dstImage, dstSubresourceRange, dstImage->mipLevelExtent(dstSubresource.mipLevel) == dstExtent ? VK_IMAGE_LAYOUT_UNDEFINED : dstImage->info().layout, dstImage->info().stages, dstImage->info().access, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); m_barriers.recordCommands(m_cmd); VkBufferImageCopy copyRegion; copyRegion.bufferOffset = srcSlice.offset(); copyRegion.bufferRowLength = srcExtent.width; copyRegion.bufferImageHeight = srcExtent.height; copyRegion.imageSubresource = dstSubresource; copyRegion.imageOffset = dstOffset; copyRegion.imageExtent = dstExtent; m_cmd->cmdCopyBufferToImage( srcSlice.handle(), dstImage->handle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Region); m_barriers.accessImage( dstImage, dstSubresourceRange, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, dstImage->info().layout, dstImage->info().stages, dstImage->info().access); m_barriers.accessBuffer(srcSlice, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, srcBuffer->info().stages, srcBuffer->info().access); m_barriers.recordCommands(m_cmd); m_cmd->trackResource(dstImage); m_cmd->trackResource(srcSlice.resource()); } void DxvkContext::copyImage( const Rc& dstImage, VkImageSubresourceLayers dstSubresource, VkOffset3D dstOffset, const Rc& srcImage, VkImageSubresourceLayers srcSubresource, VkOffset3D srcOffset, VkExtent3D extent) { this->renderPassEnd(); const VkImageSubresourceRange dstSubresourceRange = { dstSubresource.aspectMask, dstSubresource.mipLevel, 1, dstSubresource.baseArrayLayer, dstSubresource.layerCount }; const VkImageSubresourceRange srcSubresourceRange = { srcSubresource.aspectMask, srcSubresource.mipLevel, 1, srcSubresource.baseArrayLayer, srcSubresource.layerCount }; m_barriers.accessImage( dstImage, dstSubresourceRange, dstImage->mipLevelExtent(dstSubresource.mipLevel) == extent ? VK_IMAGE_LAYOUT_UNDEFINED : dstImage->info().layout, dstImage->info().stages, dstImage->info().access, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); m_barriers.accessImage( srcImage, srcSubresourceRange, srcImage->info().layout, srcImage->info().stages, srcImage->info().access, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT); m_barriers.recordCommands(m_cmd); VkImageCopy imageRegion; imageRegion.srcSubresource = srcSubresource; imageRegion.srcOffset = srcOffset; imageRegion.dstSubresource = dstSubresource; imageRegion.dstOffset = dstOffset; imageRegion.extent = extent; m_cmd->cmdCopyImage( srcImage->handle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage->handle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageRegion); m_barriers.accessImage( dstImage, dstSubresourceRange, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, dstImage->info().layout, dstImage->info().stages, dstImage->info().access); m_barriers.accessImage( srcImage, srcSubresourceRange, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, srcImage->info().layout, srcImage->info().stages, srcImage->info().access); m_barriers.recordCommands(m_cmd); m_cmd->trackResource(dstImage); m_cmd->trackResource(srcImage); } void DxvkContext::copyImageToBuffer( const Rc& dstBuffer, VkDeviceSize dstOffset, VkExtent2D dstExtent, const Rc& srcImage, VkImageSubresourceLayers srcSubresource, VkOffset3D srcOffset, VkExtent3D srcExtent) { this->renderPassEnd(); auto dstSlice = dstBuffer->subSlice(dstOffset, 0); const VkImageSubresourceRange srcSubresourceRange = { srcSubresource.aspectMask, srcSubresource.mipLevel, 1, srcSubresource.baseArrayLayer, srcSubresource.layerCount }; m_barriers.accessImage( srcImage, srcSubresourceRange, srcImage->info().layout, srcImage->info().stages, srcImage->info().access, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT); m_barriers.recordCommands(m_cmd); VkBufferImageCopy copyRegion; copyRegion.bufferOffset = dstSlice.offset(); copyRegion.bufferRowLength = dstExtent.width; copyRegion.bufferImageHeight = dstExtent.height; copyRegion.imageSubresource = srcSubresource; copyRegion.imageOffset = srcOffset; copyRegion.imageExtent = srcExtent; m_cmd->cmdCopyImageToBuffer( srcImage->handle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstSlice.handle(), 1, ©Region); m_barriers.accessImage( srcImage, srcSubresourceRange, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, srcImage->info().layout, srcImage->info().stages, srcImage->info().access); m_barriers.accessBuffer(dstSlice, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, dstBuffer->info().stages, dstBuffer->info().access); m_barriers.recordCommands(m_cmd); m_cmd->trackResource(srcImage); m_cmd->trackResource(dstSlice.resource()); } void DxvkContext::dispatch( uint32_t x, uint32_t y, uint32_t z) { this->commitComputeState(); if (m_cpActivePipeline != VK_NULL_HANDLE) { m_cmd->cmdDispatch(x, y, z); this->commitComputeBarriers(); } } void DxvkContext::dispatchIndirect( const DxvkBufferSlice& buffer) { this->commitComputeState(); auto physicalSlice = buffer.physicalSlice(); if (m_cpActivePipeline != VK_NULL_HANDLE) { m_cmd->cmdDispatchIndirect( physicalSlice.handle(), physicalSlice.offset()); this->commitComputeBarriers(); } } void DxvkContext::draw( uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) { this->commitGraphicsState(); if (m_gpActivePipeline != VK_NULL_HANDLE) { m_cmd->cmdDraw( vertexCount, instanceCount, firstVertex, firstInstance); } } void DxvkContext::drawIndirect( const DxvkBufferSlice& buffer, uint32_t count, uint32_t stride) { this->commitGraphicsState(); if (m_gpActivePipeline != VK_NULL_HANDLE) { auto physicalSlice = buffer.physicalSlice(); m_cmd->cmdDrawIndirect( physicalSlice.handle(), physicalSlice.offset(), count, stride); } } void DxvkContext::drawIndexed( uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, uint32_t vertexOffset, uint32_t firstInstance) { this->commitGraphicsState(); if (m_gpActivePipeline != VK_NULL_HANDLE) { m_cmd->cmdDrawIndexed( indexCount, instanceCount, firstIndex, vertexOffset, firstInstance); } } void DxvkContext::drawIndexedIndirect( const DxvkBufferSlice& buffer, uint32_t count, uint32_t stride) { this->commitGraphicsState(); if (m_gpActivePipeline != VK_NULL_HANDLE) { auto physicalSlice = buffer.physicalSlice(); m_cmd->cmdDrawIndexedIndirect( physicalSlice.handle(), physicalSlice.offset(), count, stride); } } void DxvkContext::initImage( const Rc& image, const VkImageSubresourceRange& subresources) { m_barriers.accessImage(image, subresources, VK_IMAGE_LAYOUT_UNDEFINED, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, image->info().layout, image->info().stages, image->info().access); m_barriers.recordCommands(m_cmd); } void DxvkContext::generateMipmaps( const Rc& image, const VkImageSubresourceRange& subresources) { if (subresources.levelCount <= 1) return; this->renderPassEnd(); // The top-most level will only be read. We can // discard the contents of all the lower levels // since we're going to override them anyway. m_barriers.accessImage(image, VkImageSubresourceRange { subresources.aspectMask, subresources.baseMipLevel, 1, subresources.baseArrayLayer, subresources.layerCount }, image->info().layout, image->info().stages, image->info().access, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT); m_barriers.accessImage(image, VkImageSubresourceRange { subresources.aspectMask, subresources.baseMipLevel + 1, subresources.levelCount - 1, subresources.baseArrayLayer, subresources.layerCount }, VK_IMAGE_LAYOUT_UNDEFINED, image->info().stages, image->info().access, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); m_barriers.recordCommands(m_cmd); // Generate each individual mip level with a blit for (uint32_t i = 1; i < subresources.levelCount; i++) { const uint32_t mip = subresources.baseMipLevel + i; const VkExtent3D srcExtent = image->mipLevelExtent(mip - 1); const VkExtent3D dstExtent = image->mipLevelExtent(mip); VkImageBlit region; region.srcSubresource = VkImageSubresourceLayers { subresources.aspectMask, mip - 1, subresources.baseArrayLayer, subresources.layerCount }; region.srcOffsets[0] = VkOffset3D { 0, 0, 0 }; region.srcOffsets[1].x = srcExtent.width; region.srcOffsets[1].y = srcExtent.height; region.srcOffsets[1].z = srcExtent.depth; region.dstSubresource = VkImageSubresourceLayers { subresources.aspectMask, mip, subresources.baseArrayLayer, subresources.layerCount }; region.dstOffsets[0] = VkOffset3D { 0, 0, 0 }; region.dstOffsets[1].x = dstExtent.width; region.dstOffsets[1].y = dstExtent.height; region.dstOffsets[1].z = dstExtent.depth; m_cmd->cmdBlitImage( image->handle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image->handle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion, VK_FILTER_LINEAR); if (i + 1 < subresources.levelCount) { m_barriers.accessImage(image, VkImageSubresourceRange { subresources.aspectMask, mip, 1, subresources.baseArrayLayer, subresources.layerCount }, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT); m_barriers.recordCommands(m_cmd); } } // Transform mip levels back into their original layout. // The last mip level is still in TRANSFER_DST_OPTIMAL. m_barriers.accessImage(image, VkImageSubresourceRange { subresources.aspectMask, subresources.baseMipLevel, subresources.levelCount - 1, subresources.baseArrayLayer, subresources.layerCount }, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, image->info().layout, image->info().stages, image->info().access); m_barriers.accessImage(image, VkImageSubresourceRange { subresources.aspectMask, subresources.baseMipLevel + subresources.levelCount - 1, 1, subresources.baseArrayLayer, subresources.layerCount }, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, image->info().layout, image->info().stages, image->info().access); m_barriers.recordCommands(m_cmd); } void DxvkContext::invalidateBuffer( const Rc& buffer, const DxvkPhysicalBufferSlice& slice) { // Allocate new backing resource buffer->rename(slice); // We also need to update all bindings that the buffer // may be bound to either directly or through views. const VkBufferUsageFlags usage = buffer->info().usage; if (usage & VK_BUFFER_USAGE_INDEX_BUFFER_BIT) m_flags.set(DxvkContextFlag::GpDirtyIndexBuffer); if (usage & VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) m_flags.set(DxvkContextFlag::GpDirtyVertexBuffers); if (usage & (VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT)) m_flags.set(DxvkContextFlag::GpDirtyResources, DxvkContextFlag::CpDirtyResources); } void DxvkContext::resolveImage( const Rc& dstImage, const VkImageSubresourceLayers& dstSubresources, const Rc& srcImage, const VkImageSubresourceLayers& srcSubresources) { this->renderPassEnd(); VkImageSubresourceRange dstSubresourceRange = { dstSubresources.aspectMask, dstSubresources.mipLevel, 1, dstSubresources.baseArrayLayer, dstSubresources.layerCount, }; VkImageSubresourceRange srcSubresourceRange = { srcSubresources.aspectMask, srcSubresources.mipLevel, 1, srcSubresources.baseArrayLayer, srcSubresources.layerCount, }; // We only support resolving to the entire image // area, so we might as well discard its contents m_barriers.accessImage( dstImage, dstSubresourceRange, VK_IMAGE_LAYOUT_UNDEFINED, dstImage->info().stages, dstImage->info().access, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); m_barriers.accessImage( srcImage, srcSubresourceRange, srcImage->info().layout, srcImage->info().stages, srcImage->info().access, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT); m_barriers.recordCommands(m_cmd); VkImageResolve imageRegion; imageRegion.srcSubresource = srcSubresources; imageRegion.srcOffset = VkOffset3D { 0, 0, 0 }; imageRegion.dstSubresource = dstSubresources; imageRegion.dstOffset = VkOffset3D { 0, 0, 0 }; imageRegion.extent = srcImage->mipLevelExtent(srcSubresources.mipLevel); m_cmd->cmdResolveImage( srcImage->handle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage->handle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageRegion); m_barriers.accessImage( dstImage, dstSubresourceRange, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, dstImage->info().layout, dstImage->info().stages, dstImage->info().access); m_barriers.accessImage( srcImage, srcSubresourceRange, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, srcImage->info().layout, srcImage->info().stages, srcImage->info().access); m_barriers.recordCommands(m_cmd); } void DxvkContext::updateBuffer( const Rc& buffer, VkDeviceSize offset, VkDeviceSize size, const void* data) { if (size == 0) return; this->renderPassEnd(); auto physicalSlice = buffer->subSlice(offset, size); // Vulkan specifies that small amounts of data (up to 64kB) can // be copied to a buffer directly if the size is a multiple of // four. Anything else must be copied through a staging buffer. // We'll limit the size to 4kB in order to keep command buffers // reasonably small, we do not know how much data apps may upload. if ((size <= 4096) && ((size & 0x3) == 0) && ((offset & 0x3) == 0)) { m_cmd->cmdUpdateBuffer( physicalSlice.handle(), physicalSlice.offset(), physicalSlice.length(), data); } else { auto slice = m_cmd->stagedAlloc(size); std::memcpy(slice.mapPtr, data, size); m_cmd->stagedBufferCopy( physicalSlice.handle(), physicalSlice.offset(), physicalSlice.length(), slice); } m_barriers.accessBuffer( physicalSlice, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, buffer->info().stages, buffer->info().access); m_barriers.recordCommands(m_cmd); m_cmd->trackResource(buffer->resource()); } void DxvkContext::updateImage( const Rc& image, const VkImageSubresourceLayers& subresources, VkOffset3D imageOffset, VkExtent3D imageExtent, const void* data, VkDeviceSize pitchPerRow, VkDeviceSize pitchPerLayer) { this->renderPassEnd(); // Upload data through a staging buffer. Special care needs to // be taken when dealing with compressed image formats: Rather // than copying pixels, we'll be copying blocks of pixels. const DxvkFormatInfo* formatInfo = imageFormatInfo(image->info().format); // Align image extent to a full block. This is necessary in // case the image size is not a multiple of the block size. VkExtent3D elementCount = util::computeBlockCount( imageExtent, formatInfo->blockSize); elementCount.depth *= subresources.layerCount; // Allocate staging buffer memory for the image data. The // pixels or blocks will be tightly packed within the buffer. const DxvkStagingBufferSlice slice = m_cmd->stagedAlloc( formatInfo->elementSize * util::flattenImageExtent(elementCount)); auto dstData = reinterpret_cast(slice.mapPtr); auto srcData = reinterpret_cast(data); util::packImageData(dstData, srcData, elementCount, formatInfo->elementSize, pitchPerRow, pitchPerLayer); // Prepare the image layout. If the given extent covers // the entire image, we may discard its previous contents. VkImageSubresourceRange subresourceRange; subresourceRange.aspectMask = subresources.aspectMask; subresourceRange.baseMipLevel = subresources.mipLevel; subresourceRange.levelCount = 1; subresourceRange.baseArrayLayer = subresources.baseArrayLayer; subresourceRange.layerCount = subresources.layerCount; m_barriers.accessImage( image, subresourceRange, image->mipLevelExtent(subresources.mipLevel) == imageExtent ? VK_IMAGE_LAYOUT_UNDEFINED : image->info().layout, image->info().stages, image->info().access, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); m_barriers.recordCommands(m_cmd); // Copy contents of the staging buffer into the image. // Since our source data is tightly packed, we do not // need to specify any strides. VkBufferImageCopy region; region.bufferOffset = slice.offset; region.bufferRowLength = 0; region.bufferImageHeight = 0; region.imageSubresource = subresources; region.imageOffset = imageOffset; region.imageExtent = imageExtent; m_cmd->stagedBufferImageCopy(image->handle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region, slice); // Transition image back into its optimal layout m_barriers.accessImage( image, subresourceRange, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, image->info().layout, image->info().stages, image->info().access); m_barriers.recordCommands(m_cmd); m_cmd->trackResource(image); } void DxvkContext::setViewports( uint32_t viewportCount, const VkViewport* viewports, const VkRect2D* scissorRects) { if (m_state.gp.state.rsViewportCount != viewportCount) { m_state.gp.state.rsViewportCount = viewportCount; m_flags.set(DxvkContextFlag::GpDirtyPipelineState); } for (uint32_t i = 0; i < viewportCount; i++) { m_state.vp.viewports[i] = viewports[i]; m_state.vp.scissorRects[i] = scissorRects[i]; } this->updateViewports(); } void DxvkContext::setBlendConstants( const DxvkBlendConstants& blendConstants) { for (uint32_t i = 0; i < 4; i++) m_state.om.blendConstants = blendConstants; this->updateBlendConstants(); } void DxvkContext::setStencilReference( const uint32_t reference) { m_state.om.stencilReference = reference; this->updateStencilReference(); } void DxvkContext::setInputAssemblyState(const DxvkInputAssemblyState& ia) { m_state.gp.state.iaPrimitiveTopology = ia.primitiveTopology; m_state.gp.state.iaPrimitiveRestart = ia.primitiveRestart; m_flags.set(DxvkContextFlag::GpDirtyPipelineState); } void DxvkContext::setInputLayout( uint32_t attributeCount, const DxvkVertexAttribute* attributes, uint32_t bindingCount, const DxvkVertexBinding* bindings) { m_flags.set( DxvkContextFlag::GpDirtyPipelineState, DxvkContextFlag::GpDirtyVertexBuffers); for (uint32_t i = 0; i < attributeCount; i++) { m_state.gp.state.ilAttributes[i].location = attributes[i].location; m_state.gp.state.ilAttributes[i].binding = attributes[i].binding; m_state.gp.state.ilAttributes[i].format = attributes[i].format; m_state.gp.state.ilAttributes[i].offset = attributes[i].offset; } for (uint32_t i = attributeCount; i < m_state.gp.state.ilAttributeCount; i++) m_state.gp.state.ilAttributes[i] = VkVertexInputAttributeDescription(); for (uint32_t i = 0; i < bindingCount; i++) { m_state.gp.state.ilBindings[i].binding = bindings[i].binding; m_state.gp.state.ilBindings[i].inputRate = bindings[i].inputRate; } for (uint32_t i = bindingCount; i < m_state.gp.state.ilBindingCount; i++) m_state.gp.state.ilBindings[i] = VkVertexInputBindingDescription(); m_state.gp.state.ilAttributeCount = attributeCount; m_state.gp.state.ilBindingCount = bindingCount; } void DxvkContext::setRasterizerState(const DxvkRasterizerState& rs) { m_state.gp.state.rsEnableDepthClamp = rs.enableDepthClamp; m_state.gp.state.rsEnableDiscard = rs.enableDiscard; m_state.gp.state.rsPolygonMode = rs.polygonMode; m_state.gp.state.rsCullMode = rs.cullMode; m_state.gp.state.rsFrontFace = rs.frontFace; m_state.gp.state.rsDepthBiasEnable = rs.depthBiasEnable; m_state.gp.state.rsDepthBiasConstant = rs.depthBiasConstant; m_state.gp.state.rsDepthBiasClamp = rs.depthBiasClamp; m_state.gp.state.rsDepthBiasSlope = rs.depthBiasSlope; m_flags.set(DxvkContextFlag::GpDirtyPipelineState); } void DxvkContext::setMultisampleState(const DxvkMultisampleState& ms) { m_state.gp.state.msSampleMask = ms.sampleMask; m_state.gp.state.msEnableAlphaToCoverage = ms.enableAlphaToCoverage; m_state.gp.state.msEnableAlphaToOne = ms.enableAlphaToOne; m_state.gp.state.msEnableSampleShading = ms.enableSampleShading; m_state.gp.state.msMinSampleShading = ms.minSampleShading; m_flags.set(DxvkContextFlag::GpDirtyPipelineState); } void DxvkContext::setDepthStencilState(const DxvkDepthStencilState& ds) { m_state.gp.state.dsEnableDepthTest = ds.enableDepthTest; m_state.gp.state.dsEnableDepthWrite = ds.enableDepthWrite; m_state.gp.state.dsEnableDepthBounds = ds.enableDepthBounds; m_state.gp.state.dsEnableStencilTest = ds.enableStencilTest; m_state.gp.state.dsDepthCompareOp = ds.depthCompareOp; m_state.gp.state.dsStencilOpFront = ds.stencilOpFront; m_state.gp.state.dsStencilOpBack = ds.stencilOpBack; m_state.gp.state.dsDepthBoundsMin = ds.depthBoundsMin; m_state.gp.state.dsDepthBoundsMax = ds.depthBoundsMax; m_flags.set(DxvkContextFlag::GpDirtyPipelineState); } void DxvkContext::setLogicOpState(const DxvkLogicOpState& lo) { m_state.gp.state.omEnableLogicOp = lo.enableLogicOp; m_state.gp.state.omLogicOp = lo.logicOp; m_flags.set(DxvkContextFlag::GpDirtyPipelineState); } void DxvkContext::setBlendMode( uint32_t attachment, const DxvkBlendMode& blendMode) { m_state.gp.state.omBlendAttachments[attachment].blendEnable = blendMode.enableBlending; m_state.gp.state.omBlendAttachments[attachment].srcColorBlendFactor = blendMode.colorSrcFactor; m_state.gp.state.omBlendAttachments[attachment].dstColorBlendFactor = blendMode.colorDstFactor; m_state.gp.state.omBlendAttachments[attachment].colorBlendOp = blendMode.colorBlendOp; m_state.gp.state.omBlendAttachments[attachment].srcAlphaBlendFactor = blendMode.alphaSrcFactor; m_state.gp.state.omBlendAttachments[attachment].dstAlphaBlendFactor = blendMode.alphaDstFactor; m_state.gp.state.omBlendAttachments[attachment].alphaBlendOp = blendMode.alphaBlendOp; m_state.gp.state.omBlendAttachments[attachment].colorWriteMask = blendMode.writeMask; m_flags.set(DxvkContextFlag::GpDirtyPipelineState); } void DxvkContext::renderPassBegin() { if (!m_flags.test(DxvkContextFlag::GpRenderPassBound) && (m_state.om.framebuffer != nullptr)) { m_flags.set(DxvkContextFlag::GpRenderPassBound); const DxvkFramebufferSize fbSize = m_state.om.framebuffer->size(); VkRect2D renderArea; renderArea.offset = VkOffset2D { 0, 0 }; renderArea.extent = VkExtent2D { fbSize.width, fbSize.height }; VkRenderPassBeginInfo info; info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; info.pNext = nullptr; info.renderPass = m_state.om.framebuffer->renderPass(); info.framebuffer = m_state.om.framebuffer->handle(); info.renderArea = renderArea; info.clearValueCount = 0; info.pClearValues = nullptr; m_cmd->cmdBeginRenderPass(&info, VK_SUBPASS_CONTENTS_INLINE); m_cmd->trackResource( m_state.om.framebuffer); } } void DxvkContext::renderPassEnd() { if (m_flags.test(DxvkContextFlag::GpRenderPassBound)) { m_flags.clr(DxvkContextFlag::GpRenderPassBound); m_cmd->cmdEndRenderPass(); } } void DxvkContext::updateComputePipeline() { if (m_flags.test(DxvkContextFlag::CpDirtyPipeline)) { m_flags.clr(DxvkContextFlag::CpDirtyPipeline); m_state.cp.pipeline = m_device->createComputePipeline( m_state.cp.cs.shader); m_cpActivePipeline = m_state.cp.pipeline != nullptr ? m_state.cp.pipeline->getPipelineHandle() : VK_NULL_HANDLE; if (m_state.cp.pipeline != nullptr) m_cmd->trackResource(m_state.cp.pipeline); if (m_cpActivePipeline != VK_NULL_HANDLE) { m_cmd->cmdBindPipeline( VK_PIPELINE_BIND_POINT_COMPUTE, m_cpActivePipeline); } } } void DxvkContext::updateGraphicsPipeline() { if (m_flags.test(DxvkContextFlag::GpDirtyPipeline)) { m_flags.clr(DxvkContextFlag::GpDirtyPipeline); m_state.gp.state.bsBindingState.clear(); m_state.gp.pipeline = m_device->createGraphicsPipeline( m_state.gp.vs.shader, m_state.gp.tcs.shader, m_state.gp.tes.shader, m_state.gp.gs.shader, m_state.gp.fs.shader); if (m_state.gp.pipeline != nullptr) m_cmd->trackResource(m_state.gp.pipeline); } } void DxvkContext::updateGraphicsPipelineState() { if (m_flags.test(DxvkContextFlag::GpDirtyPipelineState)) { m_flags.clr(DxvkContextFlag::GpDirtyPipelineState); for (uint32_t i = 0; i < m_state.gp.state.ilBindingCount; i++) { m_state.gp.state.ilBindings[i].stride = m_state.vi.vertexStrides[m_state.gp.state.ilBindings[i].binding]; } for (uint32_t i = m_state.gp.state.ilBindingCount; i < MaxNumVertexBindings; i++) m_state.gp.state.ilBindings[i].stride = 0; m_gpActivePipeline = m_state.gp.pipeline != nullptr ? m_state.gp.pipeline->getPipelineHandle(m_state.gp.state) : VK_NULL_HANDLE; if (m_gpActivePipeline != VK_NULL_HANDLE) { m_cmd->cmdBindPipeline( VK_PIPELINE_BIND_POINT_GRAPHICS, m_gpActivePipeline); } } } void DxvkContext::updateComputeShaderResources() { if (m_flags.test(DxvkContextFlag::CpDirtyResources)) { if (m_state.cp.pipeline != nullptr) { this->updateShaderResources( VK_PIPELINE_BIND_POINT_COMPUTE, m_state.cp.pipeline->layout()); } } } void DxvkContext::updateComputeShaderDescriptors() { if (m_flags.test(DxvkContextFlag::CpDirtyResources)) { m_flags.clr(DxvkContextFlag::CpDirtyResources); if (m_state.cp.pipeline != nullptr) { this->updateShaderDescriptors( VK_PIPELINE_BIND_POINT_COMPUTE, m_state.cp.bs, m_state.cp.pipeline->layout()); } } } void DxvkContext::updateGraphicsShaderResources() { if (m_flags.test(DxvkContextFlag::GpDirtyResources)) { if (m_state.gp.pipeline != nullptr) { this->updateShaderResources( VK_PIPELINE_BIND_POINT_GRAPHICS, m_state.gp.pipeline->layout()); } } } void DxvkContext::updateGraphicsShaderDescriptors() { if (m_flags.test(DxvkContextFlag::GpDirtyResources)) { m_flags.clr(DxvkContextFlag::GpDirtyResources); if (m_state.gp.pipeline != nullptr) { this->updateShaderDescriptors( VK_PIPELINE_BIND_POINT_GRAPHICS, m_state.gp.state.bsBindingState, m_state.gp.pipeline->layout()); } } } void DxvkContext::updateShaderResources( VkPipelineBindPoint bindPoint, const Rc& layout) { DxvkBindingState& bs = bindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS ? m_state.gp.state.bsBindingState : m_state.cp.bs; bool updatePipelineState = false; // TODO recreate resource views if the underlying // resource was marked as dirty after invalidation for (uint32_t i = 0; i < layout->bindingCount(); i++) { const auto& binding = layout->binding(i); const auto& res = m_rc[binding.slot]; switch (binding.type) { case VK_DESCRIPTOR_TYPE_SAMPLER: if (res.sampler != nullptr) { updatePipelineState |= bs.setBound(i); m_descriptors[i].image.sampler = res.sampler->handle(); m_descriptors[i].image.imageView = VK_NULL_HANDLE; m_descriptors[i].image.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; m_cmd->trackResource(res.sampler); } else { updatePipelineState |= bs.setUnbound(i); m_descriptors[i].image = m_device->dummySamplerDescriptor(); } break; case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: if (res.imageView != nullptr && res.imageView->type() == binding.view) { updatePipelineState |= bs.setBound(i); m_descriptors[i].image.sampler = VK_NULL_HANDLE; m_descriptors[i].image.imageView = res.imageView->handle(); m_descriptors[i].image.imageLayout = res.imageView->imageInfo().layout; m_cmd->trackResource(res.imageView); m_cmd->trackResource(res.imageView->image()); } else { updatePipelineState |= bs.setUnbound(i); m_descriptors[i].image = m_device->dummyImageViewDescriptor(binding.view); } break; case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: if (res.bufferView != nullptr) { updatePipelineState |= bs.setBound(i); m_descriptors[i].texelBuffer = res.bufferView->handle(); m_cmd->trackResource(res.bufferView); m_cmd->trackResource(res.bufferView->resource()); } else { updatePipelineState |= bs.setUnbound(i); m_descriptors[i].texelBuffer = m_device->dummyBufferViewDescriptor(); } break; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: if (res.bufferSlice.defined()) { updatePipelineState |= bs.setBound(i); auto physicalSlice = res.bufferSlice.physicalSlice(); m_descriptors[i].buffer.buffer = physicalSlice.handle(); m_descriptors[i].buffer.offset = physicalSlice.offset(); m_descriptors[i].buffer.range = physicalSlice.length(); m_cmd->trackResource(physicalSlice.resource()); } else { updatePipelineState |= bs.setUnbound(i); m_descriptors[i].buffer = m_device->dummyBufferDescriptor(); } break; default: Logger::err(str::format("DxvkContext: Unhandled descriptor type: ", binding.type)); } } if (updatePipelineState) { m_flags.set(bindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS ? DxvkContextFlag::GpDirtyPipelineState : DxvkContextFlag::CpDirtyPipelineState); } } void DxvkContext::updateShaderDescriptors( VkPipelineBindPoint bindPoint, const DxvkBindingState& bindingState, const Rc& layout) { std::array writes; const VkDescriptorSet dset = m_cmd->allocateDescriptorSet( layout->descriptorSetLayout()); const uint32_t bindingCount = layout->bindingCount(); for (uint32_t i = 0; i < bindingCount; i++) { writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[i].pNext = nullptr; writes[i].dstSet = dset; writes[i].dstBinding = i; writes[i].dstArrayElement = 0; writes[i].descriptorCount = 1; writes[i].descriptorType = layout->binding(i).type; writes[i].pImageInfo = &m_descriptors[i].image; writes[i].pBufferInfo = &m_descriptors[i].buffer; writes[i].pTexelBufferView = &m_descriptors[i].texelBuffer; } m_cmd->updateDescriptorSet(bindingCount, writes.data()); m_cmd->cmdBindDescriptorSet(bindPoint, layout->pipelineLayout(), dset); } void DxvkContext::updateDynamicState() { if (m_flags.test(DxvkContextFlag::GpDirtyDynamicState)) { m_flags.clr(DxvkContextFlag::GpDirtyDynamicState); this->updateViewports(); this->updateBlendConstants(); this->updateStencilReference(); } } void DxvkContext::updateViewports() { m_cmd->cmdSetViewport(0, m_state.gp.state.rsViewportCount, m_state.vp.viewports.data()); m_cmd->cmdSetScissor (0, m_state.gp.state.rsViewportCount, m_state.vp.scissorRects.data()); } void DxvkContext::updateBlendConstants() { m_cmd->cmdSetBlendConstants(&m_state.om.blendConstants.r); } void DxvkContext::updateStencilReference() { m_cmd->cmdSetStencilReference( VK_STENCIL_FRONT_AND_BACK, m_state.om.stencilReference); } void DxvkContext::updateIndexBufferBinding() { if (m_flags.test(DxvkContextFlag::GpDirtyIndexBuffer)) { m_flags.clr(DxvkContextFlag::GpDirtyIndexBuffer); if (m_state.vi.indexBuffer.defined()) { auto physicalSlice = m_state.vi.indexBuffer.physicalSlice(); m_cmd->cmdBindIndexBuffer( physicalSlice.handle(), physicalSlice.offset(), m_state.vi.indexType); m_cmd->trackResource( physicalSlice.resource()); } } } void DxvkContext::updateVertexBufferBindings() { if (m_flags.test(DxvkContextFlag::GpDirtyVertexBuffers)) { m_flags.clr(DxvkContextFlag::GpDirtyVertexBuffers); for (uint32_t i = 0; i < m_state.gp.state.ilBindingCount; i++) { const uint32_t binding = m_state.gp.state.ilBindings[i].binding; if (m_state.vi.vertexBuffers[binding].defined()) { auto vbo = m_state.vi.vertexBuffers[binding].physicalSlice(); const VkBuffer handle = vbo.handle(); const VkDeviceSize offset = vbo.offset(); m_cmd->cmdBindVertexBuffers(binding, 1, &handle, &offset); m_cmd->trackResource(vbo.resource()); } } } } void DxvkContext::commitComputeState() { // TODO handle CpDirtyPipelineState this->renderPassEnd(); this->updateComputePipeline(); this->updateComputeShaderResources(); this->updateComputeShaderDescriptors(); } void DxvkContext::commitGraphicsState() { this->renderPassBegin(); this->updateGraphicsPipeline(); this->updateDynamicState(); this->updateIndexBufferBinding(); this->updateVertexBufferBindings(); this->updateGraphicsShaderResources(); this->updateGraphicsPipelineState(); this->updateGraphicsShaderDescriptors(); } void DxvkContext::commitComputeBarriers() { // TODO optimize. Each pipeline layout should // hold a list of resource that can be written. // TODO generalize so that this can be used for // graphics pipelines as well auto layout = m_state.cp.pipeline->layout(); for (uint32_t i = 0; i < layout->bindingCount(); i++) { const DxvkDescriptorSlot binding = layout->binding(i); const DxvkShaderResourceSlot& slot = m_rc[binding.slot]; if (binding.type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) { m_barriers.accessBuffer( slot.bufferSlice.physicalSlice(), VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, slot.bufferSlice.bufferInfo().stages, slot.bufferSlice.bufferInfo().access); } else if (binding.type == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) { m_barriers.accessBuffer( slot.bufferView->slice(), VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, slot.bufferView->bufferInfo().stages, slot.bufferView->bufferInfo().access); } else if (binding.type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) { m_barriers.accessImage( slot.imageView->image(), slot.imageView->subresources(), slot.imageView->imageInfo().layout, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, slot.imageView->imageInfo().layout, slot.imageView->imageInfo().stages, slot.imageView->imageInfo().access); } } m_barriers.recordCommands(m_cmd); } }