mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-12 17:19:28 +01:00
Vulkan, OpenGL, Metal: Let draws bind a second vertex stream
Vulkan pipelines can have a second vertex binding, and draws a second vertex buffer for it. OpenGL input layout entries can come from a second buffer, with its own stride, given with the draw. Metal gets a second vertex buffer index. Nothing uses them yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0051dca319
commit
39b7e8dc2f
9 files changed
+65
-20
No files matched your search
@@ -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<MTLBuffer> vertexBuffers[METAL_VERTEX_BUFFER_INDEX + 1];
|
||||
uint32_t vertexBufferOffsets[METAL_VERTEX_BUFFER_INDEX + 1];
|
||||
__unsafe_unretained id<MTLBuffer> vertexBuffers[METAL_VERTEX_BUFFER_INDEX1 + 1];
|
||||
uint32_t vertexBufferOffsets[METAL_VERTEX_BUFFER_INDEX1 + 1];
|
||||
__unsafe_unretained id<MTLBuffer> fragmentBuffers[METAL_MAX_BUFFERS];
|
||||
uint32_t fragmentBufferOffsets[METAL_MAX_BUFFERS];
|
||||
__unsafe_unretained id<MTLTexture> fragmentTextures[METAL_MAX_TEXTURES];
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Entry> entries;
|
||||
int stride;
|
||||
int stride1 = 0;
|
||||
int semanticsMask_ = 0;
|
||||
};
|
||||
|
||||
@@ -355,13 +358,14 @@ public:
|
||||
return step.create_program.program;
|
||||
}
|
||||
|
||||
GLRInputLayout *CreateInputLayout(const std::vector<GLRInputLayout::Entry> &entries, int stride) {
|
||||
GLRInputLayout *CreateInputLayout(const std::vector<GLRInputLayout::Entry> &entries, int stride, int stride1 = 0) {
|
||||
std::lock_guard<std::mutex> 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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in new issue
Block a user