void VulkanReplay::RenderMesh(uint32_t eventId, const vector<MeshFormat> &secondaryDraws, const MeshDisplay &cfg) { if(cfg.position.vertexResourceId == ResourceId() || cfg.position.numIndices == 0) return; auto it = m_OutputWindows.find(m_ActiveWinID); if(m_ActiveWinID == 0 || it == m_OutputWindows.end()) return; OutputWindow &outw = it->second; // if the swapchain failed to create, do nothing. We will try to recreate it // again in CheckResizeOutputWindow (once per render 'frame') if(outw.swap == VK_NULL_HANDLE) return; VkDevice dev = m_pDriver->GetDev(); VkCommandBuffer cmd = m_pDriver->GetNextCmd(); const VkLayerDispatchTable *vt = ObjDisp(dev); VkResult vkr = VK_SUCCESS; VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT}; vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo); RDCASSERTEQUAL(vkr, VK_SUCCESS); VkRenderPassBeginInfo rpbegin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, NULL, Unwrap(outw.rpdepth), Unwrap(outw.fbdepth), {{ 0, 0, }, {m_DebugWidth, m_DebugHeight}}, 0, NULL, }; vt->CmdBeginRenderPass(Unwrap(cmd), &rpbegin, VK_SUBPASS_CONTENTS_INLINE); VkViewport viewport = {0.0f, 0.0f, (float)m_DebugWidth, (float)m_DebugHeight, 0.0f, 1.0f}; vt->CmdSetViewport(Unwrap(cmd), 0, 1, &viewport); Matrix4f projMat = Matrix4f::Perspective(90.0f, 0.1f, 100000.0f, float(m_DebugWidth) / float(m_DebugHeight)); Matrix4f InvProj = projMat.Inverse(); Matrix4f camMat = cfg.cam ? ((Camera *)cfg.cam)->GetMatrix() : Matrix4f::Identity(); Matrix4f ModelViewProj = projMat.Mul(camMat); Matrix4f guessProjInv; if(cfg.position.unproject) { // the derivation of the projection matrix might not be right (hell, it could be an // orthographic projection). But it'll be close enough likely. Matrix4f guessProj = cfg.position.farPlane != FLT_MAX ? Matrix4f::Perspective(cfg.fov, cfg.position.nearPlane, cfg.position.farPlane, cfg.aspect) : Matrix4f::ReversePerspective(cfg.fov, cfg.position.nearPlane, cfg.aspect); if(cfg.ortho) { guessProj = Matrix4f::Orthographic(cfg.position.nearPlane, cfg.position.farPlane); } guessProjInv = guessProj.Inverse(); ModelViewProj = projMat.Mul(camMat.Mul(guessProjInv)); } if(!secondaryDraws.empty()) { size_t mapsUsed = 0; for(size_t i = 0; i < secondaryDraws.size(); i++) { const MeshFormat &fmt = secondaryDraws[i]; if(fmt.vertexResourceId != ResourceId()) { // TODO should move the color to a push constant so we don't have to map all the time uint32_t uboOffs = 0; MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); data->mvp = ModelViewProj; data->color = Vec4f(fmt.meshColor.x, fmt.meshColor.y, fmt.meshColor.z, fmt.meshColor.w); data->homogenousInput = cfg.position.unproject; data->pointSpriteSize = Vec2f(0.0f, 0.0f); data->displayFormat = MESHDISPLAY_SOLID; data->rawoutput = 0; m_MeshRender.UBO.Unmap(); mapsUsed++; if(mapsUsed + 1 >= m_MeshRender.UBO.GetRingCount()) { // flush and sync so we can use more maps vt->CmdEndRenderPass(Unwrap(cmd)); vkr = vt->EndCommandBuffer(Unwrap(cmd)); RDCASSERTEQUAL(vkr, VK_SUCCESS); m_pDriver->SubmitCmds(); m_pDriver->FlushQ(); mapsUsed = 0; cmd = m_pDriver->GetNextCmd(); vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo); RDCASSERTEQUAL(vkr, VK_SUCCESS); vt->CmdBeginRenderPass(Unwrap(cmd), &rpbegin, VK_SUBPASS_CONTENTS_INLINE); vt->CmdSetViewport(Unwrap(cmd), 0, 1, &viewport); } MeshDisplayPipelines secondaryCache = GetDebugManager()->CacheMeshDisplayPipelines( m_MeshRender.PipeLayout, secondaryDraws[i], secondaryDraws[i]); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(secondaryCache.pipes[MeshDisplayPipelines::ePipe_WireDepth])); VkBuffer vb = m_pDriver->GetResourceManager()->GetCurrentHandle<VkBuffer>(fmt.vertexResourceId); VkDeviceSize offs = fmt.vertexByteOffset; vt->CmdBindVertexBuffers(Unwrap(cmd), 0, 1, UnwrapPtr(vb), &offs); if(fmt.indexByteStride) { VkIndexType idxtype = VK_INDEX_TYPE_UINT16; if(fmt.indexByteStride == 4) idxtype = VK_INDEX_TYPE_UINT32; if(fmt.indexResourceId != ResourceId()) { VkBuffer ib = m_pDriver->GetResourceManager()->GetLiveHandle<VkBuffer>(fmt.indexResourceId); vt->CmdBindIndexBuffer(Unwrap(cmd), Unwrap(ib), fmt.indexByteOffset, idxtype); } vt->CmdDrawIndexed(Unwrap(cmd), fmt.numIndices, 1, 0, fmt.baseVertex, 0); } else { vt->CmdDraw(Unwrap(cmd), fmt.numIndices, 1, 0, 0); } } } { // flush and sync so we can use more maps vt->CmdEndRenderPass(Unwrap(cmd)); vkr = vt->EndCommandBuffer(Unwrap(cmd)); RDCASSERTEQUAL(vkr, VK_SUCCESS); m_pDriver->SubmitCmds(); m_pDriver->FlushQ(); cmd = m_pDriver->GetNextCmd(); vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo); RDCASSERTEQUAL(vkr, VK_SUCCESS); vt->CmdBeginRenderPass(Unwrap(cmd), &rpbegin, VK_SUBPASS_CONTENTS_INLINE); vt->CmdSetViewport(Unwrap(cmd), 0, 1, &viewport); } } MeshDisplayPipelines cache = GetDebugManager()->CacheMeshDisplayPipelines( m_MeshRender.PipeLayout, cfg.position, cfg.second); if(cfg.position.vertexResourceId != ResourceId()) { VkBuffer vb = m_pDriver->GetResourceManager()->GetCurrentHandle<VkBuffer>(cfg.position.vertexResourceId); VkDeviceSize offs = cfg.position.vertexByteOffset; // we source all data from the first instanced value in the instanced case, so make sure we // offset correctly here. if(cfg.position.instanced) offs += cfg.position.vertexByteStride * (cfg.curInstance / cfg.position.instStepRate); vt->CmdBindVertexBuffers(Unwrap(cmd), 0, 1, UnwrapPtr(vb), &offs); } SolidShade solidShadeMode = cfg.solidShadeMode; // can't support secondary shading without a buffer - no pipeline will have been created if(solidShadeMode == SolidShade::Secondary && cfg.second.vertexResourceId == ResourceId()) solidShadeMode = SolidShade::NoSolid; if(solidShadeMode == SolidShade::Secondary) { VkBuffer vb = m_pDriver->GetResourceManager()->GetCurrentHandle<VkBuffer>(cfg.second.vertexResourceId); VkDeviceSize offs = cfg.second.vertexByteOffset; // we source all data from the first instanced value in the instanced case, so make sure we // offset correctly here. if(cfg.second.instanced) offs += cfg.second.vertexByteStride * (cfg.curInstance / cfg.second.instStepRate); vt->CmdBindVertexBuffers(Unwrap(cmd), 1, 1, UnwrapPtr(vb), &offs); } // solid render if(solidShadeMode != SolidShade::NoSolid && cfg.position.topology < Topology::PatchList) { VkPipeline pipe = VK_NULL_HANDLE; switch(solidShadeMode) { default: case SolidShade::Solid: pipe = cache.pipes[MeshDisplayPipelines::ePipe_SolidDepth]; break; case SolidShade::Lit: pipe = cache.pipes[MeshDisplayPipelines::ePipe_Lit]; break; case SolidShade::Secondary: pipe = cache.pipes[MeshDisplayPipelines::ePipe_Secondary]; break; } // can't support lit rendering without the pipeline - maybe geometry shader wasn't supported. if(solidShadeMode == SolidShade::Lit && pipe == VK_NULL_HANDLE) pipe = cache.pipes[MeshDisplayPipelines::ePipe_SolidDepth]; uint32_t uboOffs = 0; MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); if(solidShadeMode == SolidShade::Lit) data->invProj = projMat.Inverse(); data->mvp = ModelViewProj; data->color = Vec4f(0.8f, 0.8f, 0.0f, 1.0f); data->homogenousInput = cfg.position.unproject; data->pointSpriteSize = Vec2f(0.0f, 0.0f); data->displayFormat = (uint32_t)solidShadeMode; data->rawoutput = 0; if(solidShadeMode == SolidShade::Secondary && cfg.second.showAlpha) data->displayFormat = MESHDISPLAY_SECONDARY_ALPHA; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(pipe)); if(cfg.position.indexByteStride) { VkIndexType idxtype = VK_INDEX_TYPE_UINT16; if(cfg.position.indexByteStride == 4) idxtype = VK_INDEX_TYPE_UINT32; if(cfg.position.indexResourceId != ResourceId()) { VkBuffer ib = m_pDriver->GetResourceManager()->GetCurrentHandle<VkBuffer>(cfg.position.indexResourceId); vt->CmdBindIndexBuffer(Unwrap(cmd), Unwrap(ib), cfg.position.indexByteOffset, idxtype); } vt->CmdDrawIndexed(Unwrap(cmd), cfg.position.numIndices, 1, 0, cfg.position.baseVertex, 0); } else { vt->CmdDraw(Unwrap(cmd), cfg.position.numIndices, 1, 0, 0); } } // wireframe render if(solidShadeMode == SolidShade::NoSolid || cfg.wireframeDraw || cfg.position.topology >= Topology::PatchList) { Vec4f wireCol = Vec4f(cfg.position.meshColor.x, cfg.position.meshColor.y, cfg.position.meshColor.z, 1.0f); uint32_t uboOffs = 0; MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); data->mvp = ModelViewProj; data->color = wireCol; data->displayFormat = (uint32_t)SolidShade::Solid; data->homogenousInput = cfg.position.unproject; data->pointSpriteSize = Vec2f(0.0f, 0.0f); data->rawoutput = 0; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(cache.pipes[MeshDisplayPipelines::ePipe_WireDepth])); if(cfg.position.indexByteStride) { VkIndexType idxtype = VK_INDEX_TYPE_UINT16; if(cfg.position.indexByteStride == 4) idxtype = VK_INDEX_TYPE_UINT32; if(cfg.position.indexResourceId != ResourceId()) { VkBuffer ib = m_pDriver->GetResourceManager()->GetCurrentHandle<VkBuffer>(cfg.position.indexResourceId); vt->CmdBindIndexBuffer(Unwrap(cmd), Unwrap(ib), cfg.position.indexByteOffset, idxtype); } vt->CmdDrawIndexed(Unwrap(cmd), cfg.position.numIndices, 1, 0, cfg.position.baseVertex, 0); } else { vt->CmdDraw(Unwrap(cmd), cfg.position.numIndices, 1, 0, 0); } } MeshFormat helper; helper.indexByteStride = 2; helper.topology = Topology::LineList; helper.format.type = ResourceFormatType::Regular; helper.format.compByteWidth = 4; helper.format.compCount = 4; helper.format.compType = CompType::Float; helper.vertexByteStride = sizeof(Vec4f); // cache pipelines for use in drawing wireframe helpers cache = GetDebugManager()->CacheMeshDisplayPipelines(m_MeshRender.PipeLayout, helper, helper); if(cfg.showBBox) { Vec4f a = Vec4f(cfg.minBounds.x, cfg.minBounds.y, cfg.minBounds.z, cfg.minBounds.w); Vec4f b = Vec4f(cfg.maxBounds.x, cfg.maxBounds.y, cfg.maxBounds.z, cfg.maxBounds.w); Vec4f TLN = Vec4f(a.x, b.y, a.z, 1.0f); // TopLeftNear, etc... Vec4f TRN = Vec4f(b.x, b.y, a.z, 1.0f); Vec4f BLN = Vec4f(a.x, a.y, a.z, 1.0f); Vec4f BRN = Vec4f(b.x, a.y, a.z, 1.0f); Vec4f TLF = Vec4f(a.x, b.y, b.z, 1.0f); Vec4f TRF = Vec4f(b.x, b.y, b.z, 1.0f); Vec4f BLF = Vec4f(a.x, a.y, b.z, 1.0f); Vec4f BRF = Vec4f(b.x, a.y, b.z, 1.0f); // 12 frustum lines => 24 verts Vec4f bbox[24] = { TLN, TRN, TRN, BRN, BRN, BLN, BLN, TLN, TLN, TLF, TRN, TRF, BLN, BLF, BRN, BRF, TLF, TRF, TRF, BRF, BRF, BLF, BLF, TLF, }; VkDeviceSize vboffs = 0; Vec4f *ptr = (Vec4f *)m_MeshRender.BBoxVB.Map(vboffs); memcpy(ptr, bbox, sizeof(bbox)); m_MeshRender.BBoxVB.Unmap(); vt->CmdBindVertexBuffers(Unwrap(cmd), 0, 1, UnwrapPtr(m_MeshRender.BBoxVB.buf), &vboffs); uint32_t uboOffs = 0; MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); data->mvp = ModelViewProj; data->color = Vec4f(0.2f, 0.2f, 1.0f, 1.0f); data->displayFormat = (uint32_t)SolidShade::Solid; data->homogenousInput = 0; data->pointSpriteSize = Vec2f(0.0f, 0.0f); data->rawoutput = 0; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(cache.pipes[MeshDisplayPipelines::ePipe_WireDepth])); vt->CmdDraw(Unwrap(cmd), 24, 1, 0, 0); } // draw axis helpers if(!cfg.position.unproject) { VkDeviceSize vboffs = 0; vt->CmdBindVertexBuffers(Unwrap(cmd), 0, 1, UnwrapPtr(m_MeshRender.AxisFrustumVB.buf), &vboffs); uint32_t uboOffs = 0; MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); data->mvp = ModelViewProj; data->color = Vec4f(1.0f, 0.0f, 0.0f, 1.0f); data->displayFormat = (uint32_t)SolidShade::Solid; data->homogenousInput = 0; data->pointSpriteSize = Vec2f(0.0f, 0.0f); data->rawoutput = 0; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(cache.pipes[MeshDisplayPipelines::ePipe_Wire])); vt->CmdDraw(Unwrap(cmd), 2, 1, 0, 0); // poke the color (this would be a good candidate for a push constant) data = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); data->mvp = ModelViewProj; data->color = Vec4f(0.0f, 1.0f, 0.0f, 1.0f); data->displayFormat = (uint32_t)SolidShade::Solid; data->homogenousInput = 0; data->pointSpriteSize = Vec2f(0.0f, 0.0f); data->rawoutput = 0; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); vt->CmdDraw(Unwrap(cmd), 2, 1, 2, 0); data = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); data->mvp = ModelViewProj; data->color = Vec4f(0.0f, 0.0f, 1.0f, 1.0f); data->displayFormat = (uint32_t)SolidShade::Solid; data->homogenousInput = 0; data->pointSpriteSize = Vec2f(0.0f, 0.0f); data->rawoutput = 0; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); vt->CmdDraw(Unwrap(cmd), 2, 1, 4, 0); } // 'fake' helper frustum if(cfg.position.unproject) { VkDeviceSize vboffs = sizeof(Vec4f) * 6; // skim the axis helpers vt->CmdBindVertexBuffers(Unwrap(cmd), 0, 1, UnwrapPtr(m_MeshRender.AxisFrustumVB.buf), &vboffs); uint32_t uboOffs = 0; MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); data->mvp = ModelViewProj; data->color = Vec4f(1.0f, 1.0f, 1.0f, 1.0f); data->displayFormat = (uint32_t)SolidShade::Solid; data->homogenousInput = 0; data->pointSpriteSize = Vec2f(0.0f, 0.0f); data->rawoutput = 0; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(cache.pipes[MeshDisplayPipelines::ePipe_Wire])); vt->CmdDraw(Unwrap(cmd), 24, 1, 0, 0); } // show highlighted vertex if(cfg.highlightVert != ~0U) { { // need to end our cmd buffer, it might be submitted in GetBufferData when caching highlight // data vt->CmdEndRenderPass(Unwrap(cmd)); vkr = vt->EndCommandBuffer(Unwrap(cmd)); RDCASSERTEQUAL(vkr, VK_SUCCESS); #if ENABLED(SINGLE_FLUSH_VALIDATE) m_pDriver->SubmitCmds(); #endif } m_HighlightCache.CacheHighlightingData(eventId, cfg); { // get a new cmdbuffer and begin it cmd = m_pDriver->GetNextCmd(); vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo); RDCASSERTEQUAL(vkr, VK_SUCCESS); vt->CmdBeginRenderPass(Unwrap(cmd), &rpbegin, VK_SUBPASS_CONTENTS_INLINE); vt->CmdSetViewport(Unwrap(cmd), 0, 1, &viewport); } Topology meshtopo = cfg.position.topology; /////////////////////////////////////////////////////////////// // vectors to be set from buffers, depending on topology // this vert (blue dot, required) FloatVector activeVertex; // primitive this vert is a part of (red prim, optional) vector<FloatVector> activePrim; // for patch lists, to show other verts in patch (green dots, optional) // for non-patch lists, we use the activePrim and adjacentPrimVertices // to show what other verts are related vector<FloatVector> inactiveVertices; // adjacency (line or tri, strips or lists) (green prims, optional) // will be N*M long, N adjacent prims of M verts each. M = primSize below vector<FloatVector> adjacentPrimVertices; helper.topology = Topology::TriangleList; uint32_t primSize = 3; // number of verts per primitive if(meshtopo == Topology::LineList || meshtopo == Topology::LineStrip || meshtopo == Topology::LineList_Adj || meshtopo == Topology::LineStrip_Adj) { primSize = 2; helper.topology = Topology::LineList; } else { // update the cache, as it's currently linelist helper.topology = Topology::TriangleList; cache = GetDebugManager()->CacheMeshDisplayPipelines(m_MeshRender.PipeLayout, helper, helper); } bool valid = m_HighlightCache.FetchHighlightPositions(cfg, activeVertex, activePrim, adjacentPrimVertices, inactiveVertices); if(valid) { //////////////////////////////////////////////////////////////// // prepare rendering (for both vertices & primitives) // if data is from post transform, it will be in clipspace if(cfg.position.unproject) ModelViewProj = projMat.Mul(camMat.Mul(guessProjInv)); else ModelViewProj = projMat.Mul(camMat); MeshUBOData uniforms = {}; uniforms.mvp = ModelViewProj; uniforms.color = Vec4f(1.0f, 1.0f, 1.0f, 1.0f); uniforms.displayFormat = (uint32_t)SolidShade::Solid; uniforms.homogenousInput = cfg.position.unproject; uniforms.pointSpriteSize = Vec2f(0.0f, 0.0f); uint32_t uboOffs = 0; MeshUBOData *ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); *ubodata = uniforms; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(cache.pipes[MeshDisplayPipelines::ePipe_Solid])); //////////////////////////////////////////////////////////////// // render primitives // Draw active primitive (red) uniforms.color = Vec4f(1.0f, 0.0f, 0.0f, 1.0f); // poke the color (this would be a good candidate for a push constant) ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); *ubodata = uniforms; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); if(activePrim.size() >= primSize) { VkDeviceSize vboffs = 0; Vec4f *ptr = (Vec4f *)m_MeshRender.BBoxVB.Map(vboffs, sizeof(Vec4f) * primSize); memcpy(ptr, &activePrim[0], sizeof(Vec4f) * primSize); m_MeshRender.BBoxVB.Unmap(); vt->CmdBindVertexBuffers(Unwrap(cmd), 0, 1, UnwrapPtr(m_MeshRender.BBoxVB.buf), &vboffs); vt->CmdDraw(Unwrap(cmd), primSize, 1, 0, 0); } // Draw adjacent primitives (green) uniforms.color = Vec4f(0.0f, 1.0f, 0.0f, 1.0f); // poke the color (this would be a good candidate for a push constant) ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); *ubodata = uniforms; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); if(adjacentPrimVertices.size() >= primSize && (adjacentPrimVertices.size() % primSize) == 0) { VkDeviceSize vboffs = 0; Vec4f *ptr = (Vec4f *)m_MeshRender.BBoxVB.Map(vboffs, sizeof(Vec4f) * adjacentPrimVertices.size()); memcpy(ptr, &adjacentPrimVertices[0], sizeof(Vec4f) * adjacentPrimVertices.size()); m_MeshRender.BBoxVB.Unmap(); vt->CmdBindVertexBuffers(Unwrap(cmd), 0, 1, UnwrapPtr(m_MeshRender.BBoxVB.buf), &vboffs); vt->CmdDraw(Unwrap(cmd), (uint32_t)adjacentPrimVertices.size(), 1, 0, 0); } //////////////////////////////////////////////////////////////// // prepare to render dots float scale = 800.0f / float(m_DebugHeight); float asp = float(m_DebugWidth) / float(m_DebugHeight); uniforms.pointSpriteSize = Vec2f(scale / asp, scale); // Draw active vertex (blue) uniforms.color = Vec4f(0.0f, 0.0f, 1.0f, 1.0f); // poke the color (this would be a good candidate for a push constant) ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); *ubodata = uniforms; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); // vertices are drawn with tri strips helper.topology = Topology::TriangleStrip; cache = GetDebugManager()->CacheMeshDisplayPipelines(m_MeshRender.PipeLayout, helper, helper); FloatVector vertSprite[4] = { activeVertex, activeVertex, activeVertex, activeVertex, }; vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(cache.pipes[MeshDisplayPipelines::ePipe_Solid])); { VkDeviceSize vboffs = 0; Vec4f *ptr = (Vec4f *)m_MeshRender.BBoxVB.Map(vboffs, sizeof(vertSprite)); memcpy(ptr, &vertSprite[0], sizeof(vertSprite)); m_MeshRender.BBoxVB.Unmap(); vt->CmdBindVertexBuffers(Unwrap(cmd), 0, 1, UnwrapPtr(m_MeshRender.BBoxVB.buf), &vboffs); vt->CmdDraw(Unwrap(cmd), 4, 1, 0, 0); } // Draw inactive vertices (green) uniforms.color = Vec4f(0.0f, 1.0f, 0.0f, 1.0f); // poke the color (this would be a good candidate for a push constant) ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); *ubodata = uniforms; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); if(!inactiveVertices.empty()) { VkDeviceSize vboffs = 0; FloatVector *ptr = (FloatVector *)m_MeshRender.BBoxVB.Map(vboffs, sizeof(vertSprite)); for(size_t i = 0; i < inactiveVertices.size(); i++) { *ptr++ = inactiveVertices[i]; *ptr++ = inactiveVertices[i]; *ptr++ = inactiveVertices[i]; *ptr++ = inactiveVertices[i]; } m_MeshRender.BBoxVB.Unmap(); for(size_t i = 0; i < inactiveVertices.size(); i++) { vt->CmdBindVertexBuffers(Unwrap(cmd), 0, 1, UnwrapPtr(m_MeshRender.BBoxVB.buf), &vboffs); vt->CmdDraw(Unwrap(cmd), 4, 1, 0, 0); vboffs += sizeof(FloatVector) * 4; } } } } vt->CmdEndRenderPass(Unwrap(cmd)); vkr = vt->EndCommandBuffer(Unwrap(cmd)); RDCASSERTEQUAL(vkr, VK_SUCCESS); #if ENABLED(SINGLE_FLUSH_VALIDATE) m_pDriver->SubmitCmds(); #endif }
void VulkanDebugManager::CopyDepthArrayToTex2DMS(VkImage destMS, VkImage srcArray, VkExtent3D extent, uint32_t layers, uint32_t samples, VkFormat fmt) { VkImageAspectFlags aspectFlags = VK_IMAGE_ASPECT_DEPTH_BIT; int pipeIndex = 0; switch(fmt) { case VK_FORMAT_D16_UNORM: pipeIndex = 0; break; case VK_FORMAT_D16_UNORM_S8_UINT: pipeIndex = 1; aspectFlags |= VK_IMAGE_ASPECT_STENCIL_BIT; break; case VK_FORMAT_X8_D24_UNORM_PACK32: pipeIndex = 2; break; case VK_FORMAT_D24_UNORM_S8_UINT: pipeIndex = 3; aspectFlags |= VK_IMAGE_ASPECT_STENCIL_BIT; break; case VK_FORMAT_D32_SFLOAT: pipeIndex = 4; break; case VK_FORMAT_D32_SFLOAT_S8_UINT: pipeIndex = 5; aspectFlags |= VK_IMAGE_ASPECT_STENCIL_BIT; break; default: RDCERR("Unexpected depth format: %d", fmt); return; } // 0-based from 2x MSAA uint32_t sampleIndex = SampleIndex((VkSampleCountFlagBits)samples) - 1; if(sampleIndex >= ARRAY_COUNT(m_DepthArray2MSPipe[0])) { RDCERR("Unsupported sample count %u", samples); return; } VkPipeline pipe = m_DepthArray2MSPipe[pipeIndex][sampleIndex]; if(pipe == VK_NULL_HANDLE) return; VkDevice dev = m_Device; VkResult vkr = VK_SUCCESS; VkImageView srcDepthView = VK_NULL_HANDLE, srcStencilView = VK_NULL_HANDLE; VkImageView *destView = new VkImageView[layers]; VkImageViewCreateInfo viewInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, NULL, 0, srcArray, VK_IMAGE_VIEW_TYPE_2D_ARRAY, fmt, {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO}, { VK_IMAGE_ASPECT_DEPTH_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS, }, }; vkr = ObjDisp(dev)->CreateImageView(Unwrap(dev), &viewInfo, NULL, &srcDepthView); RDCASSERTEQUAL(vkr, VK_SUCCESS); if(aspectFlags & VK_IMAGE_ASPECT_STENCIL_BIT) { viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; vkr = ObjDisp(dev)->CreateImageView(Unwrap(dev), &viewInfo, NULL, &srcStencilView); RDCASSERTEQUAL(vkr, VK_SUCCESS); } viewInfo.subresourceRange.aspectMask = aspectFlags; viewInfo.image = destMS; viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; for(uint32_t i = 0; i < layers; i++) { viewInfo.subresourceRange.baseArrayLayer = i; viewInfo.subresourceRange.layerCount = 1; vkr = ObjDisp(dev)->CreateImageView(Unwrap(dev), &viewInfo, NULL, &destView[i]); RDCASSERTEQUAL(vkr, VK_SUCCESS); } VkDescriptorImageInfo srcdesc[2]; srcdesc[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; srcdesc[0].imageView = srcDepthView; srcdesc[0].sampler = Unwrap(m_ArrayMSSampler); // not used - we use texelFetch srcdesc[1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; srcdesc[1].imageView = srcStencilView; srcdesc[1].sampler = Unwrap(m_ArrayMSSampler); // not used - we use texelFetch VkWriteDescriptorSet writeSet[] = { {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_ArrayMSDescSet), 0, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &srcdesc[0], NULL, NULL}, {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_ArrayMSDescSet), 1, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &srcdesc[1], NULL, NULL}, }; if(aspectFlags & VK_IMAGE_ASPECT_STENCIL_BIT) ObjDisp(dev)->UpdateDescriptorSets(Unwrap(dev), 2, writeSet, 0, NULL); else ObjDisp(dev)->UpdateDescriptorSets(Unwrap(dev), 1, writeSet, 0, NULL); // create a bespoke framebuffer and renderpass for rendering VkAttachmentDescription attDesc = {0, fmt, (VkSampleCountFlagBits)samples, VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL}; VkAttachmentReference attRef = {0, VK_IMAGE_LAYOUT_GENERAL}; VkSubpassDescription sub = {}; sub.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; sub.pDepthStencilAttachment = &attRef; VkRenderPassCreateInfo rpinfo = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, NULL, 0, 1, &attDesc, 1, &sub, 0, NULL, // dependencies }; VkRenderPass rp = VK_NULL_HANDLE; ObjDisp(dev)->CreateRenderPass(Unwrap(dev), &rpinfo, NULL, &rp); VkFramebufferCreateInfo fbinfo = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, NULL, 0, rp, 1, NULL, extent.width, extent.height, 1, }; VkFramebuffer *fb = new VkFramebuffer[layers]; for(uint32_t i = 0; i < layers; i++) { fbinfo.pAttachments = destView + i; vkr = ObjDisp(dev)->CreateFramebuffer(Unwrap(dev), &fbinfo, NULL, &fb[i]); RDCASSERTEQUAL(vkr, VK_SUCCESS); } VkCommandBuffer cmd = m_pDriver->GetNextCmd(); VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT}; ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo); VkClearValue clearval = {}; VkRenderPassBeginInfo rpbegin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, NULL, rp, VK_NULL_HANDLE, {{0, 0}, {extent.width, extent.height}}, 1, &clearval, }; uint32_t numStencil = 1; if(aspectFlags & VK_IMAGE_ASPECT_STENCIL_BIT) numStencil = 256; Vec4u params; params.x = samples; params.y = 0; // currentSample; for(uint32_t i = 0; i < layers; i++) { rpbegin.framebuffer = fb[i]; ObjDisp(cmd)->CmdBeginRenderPass(Unwrap(cmd), &rpbegin, VK_SUBPASS_CONTENTS_INLINE); ObjDisp(cmd)->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(pipe)); ObjDisp(cmd)->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_ArrayMSPipeLayout), 0, 1, UnwrapPtr(m_ArrayMSDescSet), 0, NULL); VkViewport viewport = {0.0f, 0.0f, (float)extent.width, (float)extent.height, 0.0f, 1.0f}; ObjDisp(cmd)->CmdSetViewport(Unwrap(cmd), 0, 1, &viewport); params.z = i; // currentSlice; for(uint32_t s = 0; s < numStencil; s++) { params.w = numStencil == 1 ? 1000 : s; // currentStencil; ObjDisp(cmd)->CmdSetStencilReference(Unwrap(cmd), VK_STENCIL_FRONT_AND_BACK, s); ObjDisp(cmd)->CmdPushConstants(Unwrap(cmd), Unwrap(m_ArrayMSPipeLayout), VK_SHADER_STAGE_ALL, 0, sizeof(Vec4u), ¶ms); ObjDisp(cmd)->CmdDraw(Unwrap(cmd), 4, 1, 0, 0); } ObjDisp(cmd)->CmdEndRenderPass(Unwrap(cmd)); } ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd)); // submit cmds and wait for idle so we can readback m_pDriver->SubmitCmds(); m_pDriver->FlushQ(); for(uint32_t i = 0; i < layers; i++) ObjDisp(dev)->DestroyFramebuffer(Unwrap(dev), fb[i], NULL); ObjDisp(dev)->DestroyRenderPass(Unwrap(dev), rp, NULL); ObjDisp(dev)->DestroyImageView(Unwrap(dev), srcDepthView, NULL); if(srcStencilView != VK_NULL_HANDLE) ObjDisp(dev)->DestroyImageView(Unwrap(dev), srcStencilView, NULL); for(uint32_t i = 0; i < layers; i++) ObjDisp(dev)->DestroyImageView(Unwrap(dev), destView[i], NULL); SAFE_DELETE_ARRAY(destView); SAFE_DELETE_ARRAY(fb); }
VkResult WrappedVulkan::vkCreateSwapchainKHR( VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapChain) { VkSwapchainCreateInfoKHR createInfo = *pCreateInfo; // make sure we can readback to get the screenshot createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; createInfo.surface = Unwrap(createInfo.surface); createInfo.oldSwapchain = Unwrap(createInfo.oldSwapchain); VkResult ret = ObjDisp(device)->CreateSwapchainKHR(Unwrap(device), &createInfo, pAllocator, pSwapChain); if(ret == VK_SUCCESS) { ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), *pSwapChain); if(m_State >= WRITING) { Chunk *chunk = NULL; { CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CONTEXT(CREATE_SWAP_BUFFER); Serialise_vkCreateSwapchainKHR(localSerialiser, device, pCreateInfo, NULL, pSwapChain); chunk = scope.Get(); } VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pSwapChain); record->AddChunk(chunk); record->swapInfo = new SwapchainInfo(); SwapchainInfo &swapInfo = *record->swapInfo; // sneaky casting of window handle into record swapInfo.wndHandle = (RENDERDOC_WindowHandle)GetRecord(pCreateInfo->surface); { SCOPED_LOCK(m_SwapLookupLock); m_SwapLookup[swapInfo.wndHandle] = *pSwapChain; } RenderDoc::Inst().AddFrameCapturer(LayerDisp(m_Instance), swapInfo.wndHandle, this); swapInfo.format = pCreateInfo->imageFormat; swapInfo.extent = pCreateInfo->imageExtent; swapInfo.arraySize = pCreateInfo->imageArrayLayers; VkResult vkr = VK_SUCCESS; const VkLayerDispatchTable *vt = ObjDisp(device); { VkAttachmentDescription attDesc = { 0, pCreateInfo->imageFormat, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, }; VkAttachmentReference attRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; VkSubpassDescription sub = { 0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, NULL, // inputs 1, &attRef, // color NULL, // resolve NULL, // depth-stencil 0, NULL, // preserve }; VkRenderPassCreateInfo rpinfo = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, NULL, 0, 1, &attDesc, 1, &sub, 0, NULL, // dependencies }; vkr = vt->CreateRenderPass(Unwrap(device), &rpinfo, NULL, &swapInfo.rp); RDCASSERTEQUAL(vkr, VK_SUCCESS); GetResourceManager()->WrapResource(Unwrap(device), swapInfo.rp); } // serialise out the swap chain images { uint32_t numSwapImages; VkResult ret = vt->GetSwapchainImagesKHR(Unwrap(device), Unwrap(*pSwapChain), &numSwapImages, NULL); RDCASSERTEQUAL(ret, VK_SUCCESS); swapInfo.lastPresent = 0; swapInfo.images.resize(numSwapImages); for(uint32_t i=0; i < numSwapImages; i++) { swapInfo.images[i].im = VK_NULL_HANDLE; swapInfo.images[i].view = VK_NULL_HANDLE; swapInfo.images[i].fb = VK_NULL_HANDLE; } VkImage* images = new VkImage[numSwapImages]; // go through our own function so we assign these images IDs ret = vkGetSwapchainImagesKHR(device, *pSwapChain, &numSwapImages, images); RDCASSERTEQUAL(ret, VK_SUCCESS); for(uint32_t i=0; i < numSwapImages; i++) { SwapchainInfo::SwapImage &swapImInfo = swapInfo.images[i]; // memory doesn't exist for genuine WSI created images swapImInfo.im = images[i]; ResourceId imid = GetResID(images[i]); VkImageSubresourceRange range; range.baseMipLevel = range.baseArrayLayer = 0; range.levelCount = 1; range.layerCount = pCreateInfo->imageArrayLayers; range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; // fill out image info so we track resource state barriers { SCOPED_LOCK(m_ImageLayoutsLock); m_ImageLayouts[imid].subresourceStates.clear(); m_ImageLayouts[imid].subresourceStates.push_back(ImageRegionState(range, UNKNOWN_PREV_IMG_LAYOUT, VK_IMAGE_LAYOUT_UNDEFINED)); } { VkImageViewCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, NULL, 0, Unwrap(images[i]), VK_IMAGE_VIEW_TYPE_2D, pCreateInfo->imageFormat, { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }, }; vkr = vt->CreateImageView(Unwrap(device), &info, NULL, &swapImInfo.view); RDCASSERTEQUAL(vkr, VK_SUCCESS); GetResourceManager()->WrapResource(Unwrap(device), swapImInfo.view); VkFramebufferCreateInfo fbinfo = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, NULL, 0, Unwrap(swapInfo.rp), 1, UnwrapPtr(swapImInfo.view), (uint32_t)pCreateInfo->imageExtent.width, (uint32_t)pCreateInfo->imageExtent.height, 1, }; vkr = vt->CreateFramebuffer(Unwrap(device), &fbinfo, NULL, &swapImInfo.fb); RDCASSERTEQUAL(vkr, VK_SUCCESS); GetResourceManager()->WrapResource(Unwrap(device), swapImInfo.fb); } } SAFE_DELETE_ARRAY(images); } } else { GetResourceManager()->AddLiveResource(id, *pSwapChain); } } return ret; }
void VulkanDebugManager::CopyArrayToTex2DMS(VkImage destMS, VkImage srcArray, VkExtent3D extent, uint32_t layers, uint32_t samples, VkFormat fmt) { if(!m_pDriver->GetDeviceFeatures().shaderStorageImageMultisample || !m_pDriver->GetDeviceFeatures().shaderStorageImageWriteWithoutFormat) return; if(m_Array2MSPipe == VK_NULL_HANDLE) return; if(IsDepthOrStencilFormat(fmt)) { CopyDepthArrayToTex2DMS(destMS, srcArray, extent, layers, samples, fmt); return; } VkDevice dev = m_Device; VkResult vkr = VK_SUCCESS; VkImageView srcView, destView; VkImageViewCreateInfo viewInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, NULL, 0, srcArray, VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_FORMAT_UNDEFINED, {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY}, { VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS, }, }; uint32_t bs = GetByteSize(1, 1, 1, fmt, 0); if(bs == 1) viewInfo.format = VK_FORMAT_R8_UINT; else if(bs == 2) viewInfo.format = VK_FORMAT_R16_UINT; else if(bs == 4) viewInfo.format = VK_FORMAT_R32_UINT; else if(bs == 8) viewInfo.format = VK_FORMAT_R32G32_UINT; else if(bs == 16) viewInfo.format = VK_FORMAT_R32G32B32A32_UINT; if(viewInfo.format == VK_FORMAT_UNDEFINED) { RDCERR("Can't copy Array to MS with format %s", ToStr(fmt).c_str()); return; } if(IsStencilOnlyFormat(fmt)) viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; else if(IsDepthOrStencilFormat(fmt)) viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; vkr = ObjDisp(dev)->CreateImageView(Unwrap(dev), &viewInfo, NULL, &srcView); RDCASSERTEQUAL(vkr, VK_SUCCESS); viewInfo.image = destMS; viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; vkr = ObjDisp(dev)->CreateImageView(Unwrap(dev), &viewInfo, NULL, &destView); RDCASSERTEQUAL(vkr, VK_SUCCESS); VkDescriptorImageInfo srcdesc = {0}; srcdesc.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; srcdesc.imageView = srcView; srcdesc.sampler = Unwrap(m_ArrayMSSampler); // not used - we use texelFetch VkDescriptorImageInfo destdesc = {0}; destdesc.imageLayout = VK_IMAGE_LAYOUT_GENERAL; destdesc.imageView = destView; VkWriteDescriptorSet writeSet[] = { {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_ArrayMSDescSet), 0, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &srcdesc, NULL, NULL}, {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_ArrayMSDescSet), 2, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &destdesc, NULL, NULL}, }; ObjDisp(dev)->UpdateDescriptorSets(Unwrap(dev), ARRAY_COUNT(writeSet), writeSet, 0, NULL); VkCommandBuffer cmd = m_pDriver->GetNextCmd(); VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT}; ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo); ObjDisp(cmd)->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_COMPUTE, Unwrap(m_Array2MSPipe)); ObjDisp(cmd)->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_COMPUTE, Unwrap(m_ArrayMSPipeLayout), 0, 1, UnwrapPtr(m_ArrayMSDescSet), 0, NULL); Vec4u params = {samples, 0, 0, 0}; ObjDisp(cmd)->CmdPushConstants(Unwrap(cmd), Unwrap(m_ArrayMSPipeLayout), VK_SHADER_STAGE_ALL, 0, sizeof(Vec4u), ¶ms); ObjDisp(cmd)->CmdDispatch(Unwrap(cmd), extent.width, extent.height, layers * samples); ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd)); // submit cmds and wait for idle so we can readback m_pDriver->SubmitCmds(); m_pDriver->FlushQ(); ObjDisp(dev)->DestroyImageView(Unwrap(dev), srcView, NULL); ObjDisp(dev)->DestroyImageView(Unwrap(dev), destView, NULL); }