diff --git a/Common/GPU/Metal/MetalRenderManager.h b/Common/GPU/Metal/MetalRenderManager.h index f25aa48fca..1cbe2e84a3 100644 --- a/Common/GPU/Metal/MetalRenderManager.h +++ b/Common/GPU/Metal/MetalRenderManager.h @@ -49,6 +49,8 @@ // bottom. Vertex data goes above all of them. enum { METAL_VERTEX_BUFFER_INDEX = 16, + // A second vertex stream, for the pipelines with one. + METAL_VERTEX_BUFFER_INDEX1 = 17, // Push constants (only compute shaders have them) go in a buffer of their own, set with setBytes. METAL_PUSH_CONSTANT_INDEX = 15, METAL_MAX_TEXTURES = 8, @@ -527,8 +529,8 @@ private: MTLDepthClipMode depthClip; MTLViewport viewport; MTLScissorRect scissor; - __unsafe_unretained id vertexBuffers[METAL_VERTEX_BUFFER_INDEX + 1]; - uint32_t vertexBufferOffsets[METAL_VERTEX_BUFFER_INDEX + 1]; + __unsafe_unretained id vertexBuffers[METAL_VERTEX_BUFFER_INDEX1 + 1]; + uint32_t vertexBufferOffsets[METAL_VERTEX_BUFFER_INDEX1 + 1]; __unsafe_unretained id fragmentBuffers[METAL_MAX_BUFFERS]; uint32_t fragmentBufferOffsets[METAL_MAX_BUFFERS]; __unsafe_unretained id fragmentTextures[METAL_MAX_TEXTURES]; diff --git a/Common/GPU/OpenGL/GLQueueRunner.cpp b/Common/GPU/OpenGL/GLQueueRunner.cpp index 4dd998c03d..de53b7bc79 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.cpp +++ b/Common/GPU/OpenGL/GLQueueRunner.cpp @@ -1341,19 +1341,35 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last case GLRRenderCommand::DRAW: { GLRInputLayout *layout = c.draw.inputLayout; + if (attrMask != layout->semanticsMask_) { + EnableDisableVertexArrays(attrMask, layout->semanticsMask_); + attrMask = layout->semanticsMask_; + } + // Stream 1 first, so stream 0 is left bound. + if (layout->stride1) { + _dbg_assert_(c.draw.vertexBuffer1 && !c.draw.vertexBuffer1->Mapped()); + GLuint buf1 = c.draw.vertexBuffer1->buffer_; + if (buf1 != curArrayBuffer) { + glBindBuffer(GL_ARRAY_BUFFER, buf1); + curArrayBuffer = buf1; + } + for (const auto &entry : layout->entries) { + if (entry.stream == 1) { + glVertexAttribPointer(entry.location, entry.count, entry.type, entry.normalized, layout->stride1, (const void *)(c.draw.vertexOffset1 + entry.offset)); + } + } + } GLuint buf = c.draw.vertexBuffer->buffer_; _dbg_assert_(!c.draw.vertexBuffer->Mapped()); if (buf != curArrayBuffer) { glBindBuffer(GL_ARRAY_BUFFER, buf); curArrayBuffer = buf; } - if (attrMask != layout->semanticsMask_) { - EnableDisableVertexArrays(attrMask, layout->semanticsMask_); - attrMask = layout->semanticsMask_; - } for (size_t i = 0; i < layout->entries.size(); i++) { auto &entry = layout->entries[i]; - glVertexAttribPointer(entry.location, entry.count, entry.type, entry.normalized, layout->stride, (const void *)(c.draw.vertexOffset + entry.offset)); + if (entry.stream == 0) { + glVertexAttribPointer(entry.location, entry.count, entry.type, entry.normalized, layout->stride, (const void *)(c.draw.vertexOffset + entry.offset)); + } } if (c.draw.indexBuffer) { GLuint buf = c.draw.indexBuffer->buffer_; diff --git a/Common/GPU/OpenGL/GLQueueRunner.h b/Common/GPU/OpenGL/GLQueueRunner.h index 70b9459fbf..a683d04d52 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.h +++ b/Common/GPU/OpenGL/GLQueueRunner.h @@ -109,8 +109,11 @@ struct GLRRenderData { GLRInputLayout *inputLayout; GLRBuffer *vertexBuffer; GLRBuffer *indexBuffer; + // For the input layout's stream 1. + GLRBuffer *vertexBuffer1; uint32_t vertexOffset; uint32_t indexOffset; + uint32_t vertexOffset1; GLenum mode; // primitive GLint first; GLint count; diff --git a/Common/GPU/OpenGL/GLRenderManager.h b/Common/GPU/OpenGL/GLRenderManager.h index 54cab66827..ad43cfc633 100644 --- a/Common/GPU/OpenGL/GLRenderManager.h +++ b/Common/GPU/OpenGL/GLRenderManager.h @@ -204,9 +204,12 @@ public: GLenum type; GLboolean normalized; intptr_t offset; + // 1: from the draw's second vertex buffer, with stride1. + int stream = 0; }; std::vector entries; int stride; + int stride1 = 0; int semanticsMask_ = 0; }; @@ -355,13 +358,14 @@ public: return step.create_program.program; } - GLRInputLayout *CreateInputLayout(const std::vector &entries, int stride) { + GLRInputLayout *CreateInputLayout(const std::vector &entries, int stride, int stride1 = 0) { std::lock_guard lock(initStepsMutex_); GLRInitStep &step = initSteps_.push_uninitialized(); step.stepType = GLRInitStepType::CREATE_INPUT_LAYOUT; step.create_input_layout.inputLayout = new GLRInputLayout(); step.create_input_layout.inputLayout->entries = entries; step.create_input_layout.inputLayout->stride = stride; + step.create_input_layout.inputLayout->stride1 = stride1; for (auto &iter : step.create_input_layout.inputLayout->entries) { step.create_input_layout.inputLayout->semanticsMask_ |= 1 << iter.location; } @@ -798,13 +802,16 @@ public: data.clear.scissorH = scissorH; } - void Draw(GLRInputLayout *inputLayout, GLRBuffer *vertexBuffer, uint32_t vertexOffset, GLenum mode, int first, int count) { + // vertexBuffer1 is for the input layout's stream 1 entries, if it has any. + void Draw(GLRInputLayout *inputLayout, GLRBuffer *vertexBuffer, uint32_t vertexOffset, GLenum mode, int first, int count, GLRBuffer *vertexBuffer1 = nullptr, uint32_t vertexOffset1 = 0) { _dbg_assert_(vertexBuffer && curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); data.cmd = GLRRenderCommand::DRAW; data.draw.inputLayout = inputLayout; data.draw.vertexOffset = vertexOffset; data.draw.vertexBuffer = vertexBuffer; + data.draw.vertexBuffer1 = vertexBuffer1; + data.draw.vertexOffset1 = vertexOffset1; data.draw.indexBuffer = nullptr; data.draw.mode = mode; data.draw.first = first; @@ -815,13 +822,15 @@ public: // Would really love to have a basevertex parameter, but impossible in unextended GLES, without glDrawElementsBaseVertex, unfortunately. // If maxIndex is known (>= 0), it's passed on to the driver through glDrawRangeElements, which saves drivers that need the index // range (like Panfrost) from scanning the index data on the CPU for every draw. - void DrawIndexed(GLRInputLayout *inputLayout, GLRBuffer *vertexBuffer, uint32_t vertexOffset, GLRBuffer *indexBuffer, uint32_t indexOffset, GLenum mode, int count, GLenum indexType, int instances = 1, int maxIndex = -1) { + void DrawIndexed(GLRInputLayout *inputLayout, GLRBuffer *vertexBuffer, uint32_t vertexOffset, GLRBuffer *indexBuffer, uint32_t indexOffset, GLenum mode, int count, GLenum indexType, int instances = 1, int maxIndex = -1, GLRBuffer *vertexBuffer1 = nullptr, uint32_t vertexOffset1 = 0) { _dbg_assert_(vertexBuffer && indexBuffer && curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); data.cmd = GLRRenderCommand::DRAW; data.draw.inputLayout = inputLayout; data.draw.vertexOffset = vertexOffset; data.draw.vertexBuffer = vertexBuffer; + data.draw.vertexBuffer1 = vertexBuffer1; + data.draw.vertexOffset1 = vertexOffset1; data.draw.indexBuffer = indexBuffer; data.draw.indexOffset = indexOffset; data.draw.mode = mode; diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.cpp b/Common/GPU/Vulkan/VulkanQueueRunner.cpp index c70cc79406..109a79fdde 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.cpp +++ b/Common/GPU/Vulkan/VulkanQueueRunner.cpp @@ -1374,8 +1374,9 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c _dbg_assert_(set != VK_NULL_HANDLE); vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &set, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); - VkDeviceSize voffset = c.drawIndexed.voffset; - vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &voffset); + const VkBuffer vbuffers[2] = { c.drawIndexed.vbuffer, c.drawIndexed.vbuffer1 }; + const VkDeviceSize voffsets[2] = { c.drawIndexed.voffset, c.drawIndexed.voffset1 }; + vkCmdBindVertexBuffers(cmd, 0, c.drawIndexed.vbuffer1 ? 2 : 1, vbuffers, voffsets); vkCmdDrawIndexed(cmd, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0); } break; @@ -1387,7 +1388,9 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c _dbg_assert_(set != VK_NULL_HANDLE); vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &set, c.draw.numUboOffsets, c.draw.uboOffsets); if (c.draw.vbuffer) { - vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset); + const VkBuffer vbuffers[2] = { c.draw.vbuffer, c.draw.vbuffer1 }; + const VkDeviceSize voffsets[2] = { c.draw.voffset, c.draw.voffset1 }; + vkCmdBindVertexBuffers(cmd, 0, c.draw.vbuffer1 ? 2 : 1, vbuffers, voffsets); } vkCmdDraw(cmd, c.draw.count, 1, c.draw.offset, 0); } diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.h b/Common/GPU/Vulkan/VulkanQueueRunner.h index 21309001ff..90e8abf76f 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.h +++ b/Common/GPU/Vulkan/VulkanQueueRunner.h @@ -69,6 +69,9 @@ struct VkRenderData { VkDeviceSize voffset; uint32_t count; uint32_t offset; + // For the second vertex stream, if any. + VkBuffer vbuffer1; + uint32_t voffset1; } draw; struct { uint32_t descSetIndex; @@ -80,6 +83,9 @@ struct VkRenderData { uint32_t voffset; uint32_t ioffset; uint32_t count; + // For the second vertex stream, if any. + VkBuffer vbuffer1; + uint32_t voffset1; } drawIndexed; struct { uint32_t clearColor; diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index aa5b9a5ffd..ea983c6f8c 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -101,7 +101,8 @@ public: VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; VkVertexInputAttributeDescription attrs[8]{}; - VkVertexInputBindingDescription ibd{}; + // The second is only used by pipelines with two vertex streams. + VkVertexInputBindingDescription ibd[2]{}; VkPipelineVertexInputStateCreateInfo vis{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; VkPipelineViewportStateCreateInfo views{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; @@ -466,7 +467,8 @@ public: return retval; } - void Draw(int descSetIndex, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0) { + // vbuffer1 is for the pipeline's second vertex stream, if it has one. + void Draw(int descSetIndex, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0, VkBuffer vbuffer1 = VK_NULL_HANDLE, uint32_t voffset1 = 0) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_); VkRenderData &data = curRenderStep_->commands.push_uninitialized(); data.cmd = VKRRenderCommand::DRAW; @@ -475,6 +477,8 @@ public: data.draw.descSetIndex = descSetIndex; data.draw.vbuffer = vbuffer; data.draw.voffset = voffset; + data.draw.vbuffer1 = vbuffer1; + data.draw.voffset1 = voffset1; data.draw.numUboOffsets = numUboOffsets; _dbg_assert_(numUboOffsets <= ARRAY_SIZE(data.draw.uboOffsets)); for (int i = 0; i < numUboOffsets; i++) @@ -482,7 +486,7 @@ public: curRenderStep_->render.numDraws++; } - void DrawIndexed(int descSetIndex, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances) { + void DrawIndexed(int descSetIndex, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkBuffer vbuffer1 = VK_NULL_HANDLE, uint32_t voffset1 = 0) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_); VkRenderData &data = curRenderStep_->commands.push_uninitialized(); data.cmd = VKRRenderCommand::DRAW_INDEXED; @@ -491,6 +495,8 @@ public: data.drawIndexed.descSetIndex = descSetIndex; data.drawIndexed.vbuffer = vbuffer; data.drawIndexed.voffset = voffset; + data.drawIndexed.vbuffer1 = vbuffer1; + data.drawIndexed.voffset1 = voffset1; data.drawIndexed.ibuffer = ibuffer; data.drawIndexed.ioffset = ioffset; data.drawIndexed.numUboOffsets = numUboOffsets; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index b3982d7303..5e93a3082f 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -1320,13 +1320,13 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char _dbg_assert_((int)input->attributes.size() == (int)input->visc.vertexAttributeDescriptionCount); pipeline->stride = input->binding.stride; - gDesc.ibd = input->binding; + gDesc.ibd[0] = input->binding; for (size_t i = 0; i < input->attributes.size(); i++) { gDesc.attrs[i] = input->attributes[i]; } gDesc.vis.vertexAttributeDescriptionCount = input->visc.vertexAttributeDescriptionCount; gDesc.vis.vertexBindingDescriptionCount = input->visc.vertexBindingDescriptionCount; - gDesc.vis.pVertexBindingDescriptions = &gDesc.ibd; + gDesc.vis.pVertexBindingDescriptions = gDesc.ibd; gDesc.vis.pVertexAttributeDescriptions = gDesc.attrs; gDesc.blend0 = blend->attachments[0]; diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index 575ba1c33e..82965c9506 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -312,7 +312,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, vertexStride = (int)sizeof(TransformedVertex); } - VkVertexInputBindingDescription &ibd = desc->ibd; + VkVertexInputBindingDescription &ibd = desc->ibd[0]; ibd.binding = 0; ibd.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; ibd.stride = vertexStride; @@ -320,7 +320,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, VkPipelineVertexInputStateCreateInfo &vis = desc->vis; vis.flags = 0; vis.vertexBindingDescriptionCount = 1; - vis.pVertexBindingDescriptions = &desc->ibd; + vis.pVertexBindingDescriptions = desc->ibd; vis.vertexAttributeDescriptionCount = attributeCount; vis.pVertexAttributeDescriptions = attrs;