From bbc2f47fa83e35cac39627d684e38e59c696e083 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 13 Feb 2019 14:17:26 +0000 Subject: [PATCH] Centralise generation of buffer format strings for struct buffers * We also fix a number of issues that could cause incorrect formats to be generated. * Test cases added for D3D11/GL/Vulkan to test different struct types. These aren't automated at the moment because most of the code they're testing is in the UI itself. --- qrenderdoc/Code/FormatElement.cpp | 44 ++- .../D3D11PipelineStateViewer.cpp | 139 +------ .../PipelineState/D3D11PipelineStateViewer.h | 1 - .../D3D12PipelineStateViewer.cpp | 139 +------ .../PipelineState/GLPipelineStateViewer.cpp | 78 +--- .../PipelineState/PipelineStateViewer.cpp | 363 +++++++++++++++--- .../PipelineState/PipelineStateViewer.h | 6 + .../VulkanPipelineStateViewer.cpp | 87 +---- renderdoc/api/replay/shader_types.h | 14 +- renderdoc/driver/gl/gl_shader_refl.cpp | 76 ++-- .../d3d11/d3d11_structured_buffer_nested.cpp | 140 +++++++ util/test/demos/demos.vcxproj | 3 + util/test/demos/demos.vcxproj.filters | 9 + .../demos/gl/gl_structured_buffer_nested.cpp | 212 ++++++++++ util/test/demos/vk/vk_helpers.h | 17 + .../demos/vk/vk_structured_buffer_nested.cpp | 340 ++++++++++++++++ util/test/demos/vk/vk_test.cpp | 11 + util/test/demos/vk/vk_test.h | 2 + 18 files changed, 1134 insertions(+), 547 deletions(-) create mode 100644 util/test/demos/d3d11/d3d11_structured_buffer_nested.cpp create mode 100644 util/test/demos/gl/gl_structured_buffer_nested.cpp create mode 100644 util/test/demos/vk/vk_structured_buffer_nested.cpp diff --git a/qrenderdoc/Code/FormatElement.cpp b/qrenderdoc/Code/FormatElement.cpp index 1a525da02..5fb1b9ab5 100644 --- a/qrenderdoc/Code/FormatElement.cpp +++ b/qrenderdoc/Code/FormatElement.cpp @@ -484,13 +484,49 @@ QString FormatElement::GenerateTextureBufferFormat(const TextureDescription &tex case ResourceFormatType::Regular: { if(tex.format.compByteWidth == 1) - baseType = lit("byte"); + { + if(tex.format.compType == CompType::UNorm) + baseType = lit("unormb"); + else if(tex.format.compType == CompType::SNorm) + baseType = lit("snormb"); + else if(tex.format.compType == CompType::SInt) + baseType = lit("byte"); + else + baseType = lit("ubyte"); + } else if(tex.format.compByteWidth == 2) - baseType = lit("short"); + { + if(tex.format.compType == CompType::UNorm) + baseType = lit("unormh"); + else if(tex.format.compType == CompType::SNorm) + baseType = lit("snormh"); + else if(tex.format.compType == CompType::Float) + baseType = lit("half"); + else if(tex.format.compType == CompType::SInt) + baseType = lit("short"); + else + baseType = lit("ushort"); + } + else if(tex.format.compByteWidth == 4) + { + if(tex.format.compType == CompType::Float) + baseType = lit("float"); + else if(tex.format.compType == CompType::SInt) + baseType = lit("int"); + else + baseType = lit("uint"); + } else - baseType = lit("int"); + { + if(tex.format.compType == CompType::Float || tex.format.compType == CompType::Double) + baseType = lit("double"); + else if(tex.format.compType == CompType::SInt) + baseType = lit("long"); + else + baseType = lit("ulong"); + } - baseType = QFormatStr("rgb x%1%2").arg(baseType).arg(tex.format.compCount); + baseType = QFormatStr("rgb %1%2").arg(baseType).arg(tex.format.compCount); break; } diff --git a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp index 2a3be0190..899cc9e6e 100644 --- a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp @@ -1923,46 +1923,6 @@ void D3D11PipelineStateViewer::setState() } } -QString D3D11PipelineStateViewer::formatMembers(int indent, const QString &nameprefix, - const rdcarray &vars) -{ - QString indentstr(indent * 4, QLatin1Char(' ')); - - QString ret; - - int i = 0; - - for(const ShaderConstant &v : vars) - { - if(!v.type.members.empty()) - { - if(i > 0) - ret += lit("\n"); - ret += indentstr + lit("// struct %1\n").arg(v.type.descriptor.name); - ret += indentstr + lit("{\n") + formatMembers(indent + 1, v.name + lit("_"), v.type.members) + - indentstr + lit("}\n"); - if(i < vars.count() - 1) - ret += lit("\n"); - } - else - { - QString arr; - if(v.type.descriptor.elements > 1) - arr = QFormatStr("[%1]").arg(v.type.descriptor.elements); - ret += QFormatStr("%1%2 %3%4%5;\n") - .arg(indentstr) - .arg(v.type.descriptor.name) - .arg(nameprefix) - .arg(v.name) - .arg(arr); - } - - i++; - } - - return ret; -} - void D3D11PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, int column) { const D3D11Pipe::Shader *stage = stageForSender(item->treeWidget()); @@ -2102,103 +2062,10 @@ void D3D11PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, in if(shaderRes) { - const ShaderResource &res = *shaderRes; + format = m_Common.GenerateBufferFormatter(*shaderRes, view.res.viewFormat, offs); - if(!res.variableType.members.empty()) - { - format = lit("// struct %1\n{\n%2}") - .arg(res.variableType.descriptor.name) - .arg(formatMembers(1, QString(), res.variableType.members)); - } - else - { - const auto &desc = res.variableType.descriptor; - - if(view.res.viewFormat.type == ResourceFormatType::Undefined) - { - format = QString(); - if(desc.rowMajorStorage) - format += lit("row_major "); - - format += ToQStr(desc.type); - if(desc.rows > 1 && desc.columns > 1) - format += QFormatStr("%1x%2").arg(desc.rows).arg(desc.columns); - else if(desc.columns > 1) - format += QString::number(desc.columns); - - if(!desc.name.empty()) - format += lit(" ") + desc.name; - - if(desc.elements > 1) - format += QFormatStr("[%1]").arg(desc.elements); - } - else - { - const ResourceFormat &fmt = view.res.viewFormat; - if(fmt.type == ResourceFormatType::R10G10B10A2) - { - if(fmt.compType == CompType::UInt) - format = lit("uintten"); - if(fmt.compType == CompType::UNorm) - format = lit("unormten"); - } - else if(fmt.type == ResourceFormatType::R11G11B10) - { - format = lit("floateleven"); - } - else - { - switch(fmt.compByteWidth) - { - case 1: - { - if(fmt.compType == CompType::UNorm) - format = lit("unormb"); - if(fmt.compType == CompType::SNorm) - format = lit("snormb"); - if(fmt.compType == CompType::UInt) - format = lit("ubyte"); - if(fmt.compType == CompType::SInt) - format = lit("byte"); - break; - } - case 2: - { - if(fmt.compType == CompType::UNorm) - format = lit("unormh"); - if(fmt.compType == CompType::SNorm) - format = lit("snormh"); - if(fmt.compType == CompType::UInt) - format = lit("ushort"); - if(fmt.compType == CompType::SInt) - format = lit("short"); - if(fmt.compType == CompType::Float) - format = lit("half"); - break; - } - case 4: - { - if(fmt.compType == CompType::UNorm) - format = lit("unormf"); - if(fmt.compType == CompType::SNorm) - format = lit("snormf"); - if(fmt.compType == CompType::UInt) - format = lit("uint"); - if(fmt.compType == CompType::SInt) - format = lit("int"); - if(fmt.compType == CompType::Float) - format = lit("float"); - break; - } - } - - if(view.res.bufferFlags & D3DBufferViewFlags::Raw) - format = lit("xint"); - - format += QString::number(fmt.compCount); - } - } - } + if(view.res.bufferFlags & D3DBufferViewFlags::Raw) + format = lit("xint"); } IBufferViewer *viewer = m_Ctx.ViewBuffer(offs, size, view.res.resourceResourceId, format); diff --git a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.h index a58906230..9bc3c49ce 100644 --- a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.h @@ -104,7 +104,6 @@ private: void setEmptyRow(RDTreeWidgetItem *node); void highlightIABind(int slot); - QString formatMembers(int indent, const QString &nameprefix, const rdcarray &vars); const D3D11Pipe::Shader *stageForSender(QWidget *widget); bool HasImportantViewParams(const D3D11Pipe::View &view, TextureDescription *tex); diff --git a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp index bc7c0c130..89599a18f 100644 --- a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp @@ -1809,46 +1809,6 @@ void D3D12PipelineStateViewer::setState() } } -QString D3D12PipelineStateViewer::formatMembers(int indent, const QString &nameprefix, - const rdcarray &vars) -{ - QString indentstr(indent * 4, QLatin1Char(' ')); - - QString ret; - - int i = 0; - - for(const ShaderConstant &v : vars) - { - if(!v.type.members.empty()) - { - if(i > 0) - ret += lit("\n"); - ret += indentstr + lit("// struct %1\n").arg(v.type.descriptor.name); - ret += indentstr + lit("{\n") + formatMembers(indent + 1, v.name + lit("_"), v.type.members) + - indentstr + lit("}\n"); - if(i < vars.count() - 1) - ret += lit("\n"); - } - else - { - QString arr; - if(v.type.descriptor.elements > 1) - arr = QFormatStr("[%1]").arg(v.type.descriptor.elements); - ret += QFormatStr("%1%2 %3%4%5;\n") - .arg(indentstr) - .arg(v.type.descriptor.name) - .arg(nameprefix) - .arg(v.name) - .arg(arr); - } - - i++; - } - - return ret; -} - void D3D12PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, int column) { const D3D12Pipe::Shader *stage = stageForSender(item->treeWidget()); @@ -1954,103 +1914,10 @@ void D3D12PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, in if(shaderRes) { - const ShaderResource &res = *shaderRes; + format = m_Common.GenerateBufferFormatter(*shaderRes, view.res.viewFormat, offs); - if(!res.variableType.members.empty()) - { - format = QFormatStr("// struct %1\n{\n%2}") - .arg(res.variableType.descriptor.name) - .arg(formatMembers(1, QString(), res.variableType.members)); - } - else - { - const auto &desc = res.variableType.descriptor; - - if(view.res.viewFormat.type == ResourceFormatType::Undefined) - { - format = QString(); - if(desc.rowMajorStorage) - format += lit("row_major "); - - format += ToQStr(desc.type); - if(desc.rows > 1 && desc.columns > 1) - format += QFormatStr("%1x%2").arg(desc.rows).arg(desc.columns); - else if(desc.columns > 1) - format += QString::number(desc.columns); - - if(!desc.name.empty()) - format += lit(" ") + desc.name; - - if(desc.elements > 1) - format += QFormatStr("[%1]").arg(desc.elements); - } - else - { - const ResourceFormat &fmt = view.res.viewFormat; - if(fmt.type == ResourceFormatType::R10G10B10A2) - { - if(fmt.compType == CompType::UInt) - format = lit("uintten"); - if(fmt.compType == CompType::UNorm) - format = lit("unormten"); - } - else if(fmt.type == ResourceFormatType::R11G11B10) - { - format = lit("floateleven"); - } - else - { - switch(fmt.compByteWidth) - { - case 1: - { - if(fmt.compType == CompType::UNorm) - format = lit("unormb"); - if(fmt.compType == CompType::SNorm) - format = lit("snormb"); - if(fmt.compType == CompType::UInt) - format = lit("ubyte"); - if(fmt.compType == CompType::SInt) - format = lit("byte"); - break; - } - case 2: - { - if(fmt.compType == CompType::UNorm) - format = lit("unormh"); - if(fmt.compType == CompType::SNorm) - format = lit("snormh"); - if(fmt.compType == CompType::UInt) - format = lit("ushort"); - if(fmt.compType == CompType::SInt) - format = lit("short"); - if(fmt.compType == CompType::Float) - format = lit("half"); - break; - } - case 4: - { - if(fmt.compType == CompType::UNorm) - format = lit("unormf"); - if(fmt.compType == CompType::SNorm) - format = lit("snormf"); - if(fmt.compType == CompType::UInt) - format = lit("uint"); - if(fmt.compType == CompType::SInt) - format = lit("int"); - if(fmt.compType == CompType::Float) - format = lit("float"); - break; - } - } - - if(view.res.bufferFlags & D3DBufferViewFlags::Raw) - format = lit("xint"); - - format += QString::number(fmt.compCount); - } - } - } + if(view.res.bufferFlags & D3DBufferViewFlags::Raw) + format = lit("xint"); } IBufferViewer *viewer = m_Ctx.ViewBuffer(offs, size, view.res.resourceId, format); diff --git a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp index 941c00a42..07a13bc4a 100644 --- a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp @@ -2117,46 +2117,6 @@ void GLPipelineStateViewer::setState() } } -QString GLPipelineStateViewer::formatMembers(int indent, const QString &nameprefix, - const rdcarray &vars) -{ - QString indentstr(indent * 4, QLatin1Char(' ')); - - QString ret; - - int i = 0; - - for(const ShaderConstant &v : vars) - { - if(!v.type.members.empty()) - { - if(i > 0) - ret += lit("\n"); - ret += indentstr + QFormatStr("// struct %1\n").arg(v.type.descriptor.name); - ret += indentstr + lit("{\n") + formatMembers(indent + 1, v.name + lit("_"), v.type.members) + - indentstr + lit("}\n"); - if(i < vars.count() - 1) - ret += lit("\n"); - } - else - { - QString arr; - if(v.type.descriptor.elements > 1) - arr = QFormatStr("[%1]").arg(v.type.descriptor.elements); - ret += QFormatStr("%1%2 %3%4%5;\n") - .arg(indentstr) - .arg(v.type.descriptor.name) - .arg(nameprefix) - .arg(v.name) - .arg(arr); - } - - i++; - } - - return ret; -} - void GLPipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, int column) { const GLPipe::Shader *stage = stageForSender(item->treeWidget()); @@ -2196,43 +2156,7 @@ void GLPipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, int c const ShaderResource &shaderRes = stage->reflection->readWriteResources[buf.bindPoint]; - QString format = lit("// struct %1\n").arg(shaderRes.variableType.descriptor.name); - - if(shaderRes.variableType.members.count() > 1) - { - format += tr("// members skipped as they are fixed size:\n"); - for(int i = 0; i < shaderRes.variableType.members.count() - 1; i++) - format += QFormatStr("// %1 %2;\n") - .arg(shaderRes.variableType.members[i].type.descriptor.name) - .arg(shaderRes.variableType.members[i].name); - } - - if(!shaderRes.variableType.members.empty()) - { - format += lit("{\n") + - formatMembers(1, QString(), shaderRes.variableType.members.back().type.members) + - lit("}"); - } - else - { - const auto &desc = shaderRes.variableType.descriptor; - - format = QString(); - if(desc.rowMajorStorage) - format += lit("row_major "); - - format += ToQStr(desc.type); - if(desc.rows > 1 && desc.columns > 1) - format += QFormatStr("%1x%2").arg(desc.rows).arg(desc.columns); - else if(desc.columns > 1) - format += QString::number(desc.columns); - - if(!desc.name.empty()) - format += lit(" ") + desc.name; - - if(desc.elements > 1) - format += QFormatStr("[%1]").arg(desc.elements); - } + QString format = m_Common.GenerateBufferFormatter(shaderRes, ResourceFormat(), buf.offset); if(buf.ID != ResourceId()) { diff --git a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp index 70380386d..41b9fc2f7 100644 --- a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp @@ -39,6 +39,71 @@ #include "VulkanPipelineStateViewer.h" #include "ui_PipelineStateViewer.h" +static uint32_t byteSize(const ResourceFormat &fmt) +{ + if(fmt.Special()) + { + switch(fmt.type) + { + default: + case ResourceFormatType::R9G9B9E5: + case ResourceFormatType::R5G6B5: + case ResourceFormatType::R5G5B5A1: + case ResourceFormatType::R4G4B4A4: + case ResourceFormatType::R4G4: + case ResourceFormatType::BC1: + case ResourceFormatType::BC2: + case ResourceFormatType::BC3: + case ResourceFormatType::BC4: + case ResourceFormatType::BC5: + case ResourceFormatType::BC6: + case ResourceFormatType::BC7: + case ResourceFormatType::ETC2: + case ResourceFormatType::EAC: + case ResourceFormatType::ASTC: + case ResourceFormatType::D16S8: + case ResourceFormatType::D24S8: + case ResourceFormatType::D32S8: + case ResourceFormatType::S8: + case ResourceFormatType::YUV8: + case ResourceFormatType::YUV10: + case ResourceFormatType::YUV12: + case ResourceFormatType::YUV16: + case ResourceFormatType::PVRTC: return ~0U; + case ResourceFormatType::R10G10B10A2: + case ResourceFormatType::R11G11B10: return 4; + } + } + + return fmt.compByteWidth * fmt.compCount; +} + +static QString padding(uint32_t bytes) +{ + if(bytes == 0) + return QString(); + + QString ret; + + if(bytes > 4) + { + ret += lit("xint pad[%1];").arg(bytes / 4); + + bytes = bytes % 4; + } + + if(bytes == 4) + ret += lit("xint pad;"); + else if(bytes == 3) + ret += lit("xshort pad; xbyte pad;"); + else if(bytes == 2) + ret += lit("xshort pad;"); + else if(bytes == 1) + ret += lit("xbyte pad;"); + + return ret + lit("\n"); +} + PipelineStateViewer::PipelineStateViewer(ICaptureContext &ctx, QWidget *parent) : QFrame(parent), ui(new Ui::PipelineStateViewer), m_Ctx(ctx) { @@ -549,6 +614,241 @@ void PipelineStateViewer::setMeshViewPixmap(RDLabel *meshView) }); } +QString PipelineStateViewer::formatMembers(int indent, int requiredByteStride, + const QString &nameprefix, + const rdcarray &vars) +{ + QString indentstr(indent * 4, QLatin1Char(' ')); + + QString ret; + + int i = 0; + + for(const ShaderConstant &v : vars) + { + if(!v.type.members.empty()) + { + if(i > 0) + ret += lit("\n"); + + if(v.type.descriptor.name == "struct") + ret += indentstr + lit("// struct\n"); + else + ret += indentstr + lit("// struct %1\n").arg(v.type.descriptor.name); + + ret += indentstr + lit("{\n") + formatMembers(indent + 1, v.type.descriptor.arrayByteStride, + v.name + lit("_"), v.type.members) + + indentstr + lit("}\n"); + if(i < vars.count() - 1) + ret += lit("\n"); + } + else + { + QString arr; + if(v.type.descriptor.elements > 1) + arr = QFormatStr("[%1]").arg(v.type.descriptor.elements); + ret += QFormatStr("%1%2 %3%4%5;\n") + .arg(indentstr) + .arg(v.type.descriptor.name) + .arg(nameprefix) + .arg(v.name) + .arg(arr); + } + + i++; + } + + if(requiredByteStride > 0) + { + uint32_t structStart = 0; + + const ShaderConstant *lastChild = &vars.back(); + + structStart += lastChild->byteOffset; + while(!lastChild->type.members.isEmpty()) + { + lastChild = &lastChild->type.members.back(); + structStart += lastChild->byteOffset; + } + + uint32_t size = lastChild->type.descriptor.rows * lastChild->type.descriptor.columns; + if(lastChild->type.descriptor.type == VarType::Double || + lastChild->type.descriptor.type == VarType::ULong || + lastChild->type.descriptor.type == VarType::SLong) + size *= 8; + else if(lastChild->type.descriptor.type == VarType::Float || + lastChild->type.descriptor.type == VarType::UInt || + lastChild->type.descriptor.type == VarType::SInt) + size *= 4; + else if(lastChild->type.descriptor.type == VarType::Half || + lastChild->type.descriptor.type == VarType::UShort || + lastChild->type.descriptor.type == VarType::SShort) + size *= 2; + + if(lastChild->type.descriptor.elements > 1) + size *= lastChild->type.descriptor.elements; + + uint32_t padBytes = requiredByteStride - (lastChild->byteOffset + size); + + if(padBytes > 0) + ret += indentstr + padding(padBytes); + } + + return ret; +} + +QString PipelineStateViewer::GenerateBufferFormatter(const ShaderResource &res, + const ResourceFormat &viewFormat, + uint64_t &baseByteOffset) +{ + QString format; + + if(!res.variableType.members.empty()) + { + const rdcarray *members = &res.variableType.members; + + uint32_t requiredStride = 0; + + if(m_Ctx.APIProps().pipelineType == GraphicsAPI::Vulkan || + m_Ctx.APIProps().pipelineType == GraphicsAPI::OpenGL) + { + // GL/Vulkan allow fixed-sized members before the array-of-structs. This can't be represented + // in a buffer format so we skip it + if(members->count() > 1) + { + format += lit("// %1 members skipped as they are fixed size:\n").arg(res.name); + for(int i = 0; i < members->count() - 1; i++) + { + QString arraySize; + if(members->at(i).type.descriptor.elements > 1) + arraySize = QFormatStr("[%1]").arg(members->at(i).type.descriptor.elements); + format += QFormatStr("// %1 %2%3;\n") + .arg(members->at(i).type.descriptor.name) + .arg(members->at(i).name) + .arg(arraySize); + } + + format += lit("// final array struct @ byte offset %1\n{\n").arg(members->back().byteOffset); + + baseByteOffset += members->back().byteOffset; + } + else + { + format += lit("// struct %1\n{\n").arg(res.name); + } + + // if the last member is a struct, remove one level of indirection before declaring the + // repeated structure + if(!members->back().type.members.isEmpty()) + { + requiredStride = members->back().type.descriptor.arrayByteStride; + + members = &members->back().type.members; + } + } + else + { + format = lit("// struct %1\n{\n").arg(res.variableType.descriptor.name); + } + + format += formatMembers(1, requiredStride, QString(), *members); + format += lit("}"); + } + else + { + const auto &desc = res.variableType.descriptor; + + if(viewFormat.type == ResourceFormatType::Undefined) + { + if(desc.type == VarType::Unknown) + { + format = desc.name; + } + else + { + if(desc.rowMajorStorage) + format += lit("row_major "); + + format += ToQStr(desc.type); + if(desc.rows > 1 && desc.columns > 1) + format += QFormatStr("%1x%2").arg(desc.rows).arg(desc.columns); + else if(desc.columns > 1) + format += QString::number(desc.columns); + + if(!desc.name.empty()) + format += lit(" ") + desc.name; + + if(desc.elements > 1) + format += QFormatStr("[%1]").arg(desc.elements); + } + } + else + { + if(viewFormat.type == ResourceFormatType::R10G10B10A2) + { + if(viewFormat.compType == CompType::UInt) + format = lit("uintten"); + if(viewFormat.compType == CompType::UNorm) + format = lit("unormten"); + } + else if(viewFormat.type == ResourceFormatType::R11G11B10) + { + format = lit("floateleven"); + } + else + { + switch(viewFormat.compByteWidth) + { + case 1: + { + if(viewFormat.compType == CompType::UNorm) + format = lit("unormb"); + if(viewFormat.compType == CompType::SNorm) + format = lit("snormb"); + if(viewFormat.compType == CompType::UInt) + format = lit("ubyte"); + if(viewFormat.compType == CompType::SInt) + format = lit("byte"); + break; + } + case 2: + { + if(viewFormat.compType == CompType::UNorm) + format = lit("unormh"); + if(viewFormat.compType == CompType::SNorm) + format = lit("snormh"); + if(viewFormat.compType == CompType::UInt) + format = lit("ushort"); + if(viewFormat.compType == CompType::SInt) + format = lit("short"); + if(viewFormat.compType == CompType::Float) + format = lit("half"); + break; + } + case 4: + { + if(viewFormat.compType == CompType::UNorm) + format = lit("unormf"); + if(viewFormat.compType == CompType::SNorm) + format = lit("snormf"); + if(viewFormat.compType == CompType::UInt) + format = lit("uint"); + if(viewFormat.compType == CompType::SInt) + format = lit("int"); + if(viewFormat.compType == CompType::Float) + format = lit("float"); + break; + } + } + + format += QString::number(viewFormat.compCount); + } + } + } + + return format; +} + void PipelineStateViewer::MakeShaderVariablesHLSL(bool cbufferContents, const rdcarray &vars, QString &struct_contents, QString &struct_defs) @@ -920,69 +1220,6 @@ void PipelineStateViewer::SetupShaderEditButton(QToolButton *button, ResourceId button->setMenu(menu); } -static uint32_t byteSize(const ResourceFormat &fmt) -{ - if(fmt.Special()) - { - switch(fmt.type) - { - default: - case ResourceFormatType::R9G9B9E5: - case ResourceFormatType::R5G6B5: - case ResourceFormatType::R5G5B5A1: - case ResourceFormatType::R4G4B4A4: - case ResourceFormatType::R4G4: - case ResourceFormatType::BC1: - case ResourceFormatType::BC2: - case ResourceFormatType::BC3: - case ResourceFormatType::BC4: - case ResourceFormatType::BC5: - case ResourceFormatType::BC6: - case ResourceFormatType::BC7: - case ResourceFormatType::ETC2: - case ResourceFormatType::EAC: - case ResourceFormatType::ASTC: - case ResourceFormatType::D16S8: - case ResourceFormatType::D24S8: - case ResourceFormatType::D32S8: - case ResourceFormatType::S8: - case ResourceFormatType::YUV8: - case ResourceFormatType::YUV10: - case ResourceFormatType::YUV12: - case ResourceFormatType::YUV16: - case ResourceFormatType::PVRTC: return ~0U; - case ResourceFormatType::R10G10B10A2: - case ResourceFormatType::R11G11B10: return 4; - } - } - - return fmt.compByteWidth * fmt.compCount; -} - -static QString padding(uint32_t bytes) -{ - if(bytes == 0) - return QString(); - - QString ret; - - if(bytes > 4) - { - ret += lit("xint pad[%1];").arg(bytes / 4); - - bytes = bytes % 4; - } - - if(bytes == 3) - ret += lit("xshort pad; xbyte pad;"); - else if(bytes == 2) - ret += lit("xshort pad;"); - else if(bytes == 1) - ret += lit("xbyte pad;"); - - return ret + lit("\n"); -} - QString PipelineStateViewer::GetVBufferFormatString(uint32_t slot) { rdcarray vbs = m_Ctx.CurPipelineState().GetVBuffers(); diff --git a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h index d8139e879..f3d7d378d 100644 --- a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h @@ -70,6 +70,9 @@ public: void SetupShaderEditButton(QToolButton *button, ResourceId pipelineId, ResourceId shaderId, const ShaderReflection *shaderDetails); + QString GenerateBufferFormatter(const ShaderResource &res, const ResourceFormat &viewFormat, + uint64_t &baseByteOffset); + QString GetVBufferFormatString(uint32_t slot); void setTopologyDiagram(QLabel *diagram, Topology topo); @@ -90,6 +93,9 @@ private: QMenu *editMenus[6] = {}; + QString formatMembers(int indent, int requiredByteStride, const QString &nameprefix, + const rdcarray &vars); + QString GenerateHLSLStub(const ShaderReflection *shaderDetails, const QString &entryFunc); IShaderViewer *EditShader(ResourceId id, ShaderStage shaderType, const rdcstr &entry, ShaderCompileFlags compileFlags, ShaderEncoding encoding, diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp index dfe3429a2..285acf099 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp @@ -75,16 +75,18 @@ struct VulkanBufferTag bindPoint = 0; offset = size = 0; } - VulkanBufferTag(bool rw, uint32_t b, ResourceId id, uint64_t offs, uint64_t sz) + VulkanBufferTag(bool rw, uint32_t b, ResourceFormat f, ResourceId id, uint64_t offs, uint64_t sz) { rwRes = rw; bindPoint = b; ID = id; + fmt = f; offset = offs; size = sz; } bool rwRes; uint32_t bindPoint; + ResourceFormat fmt; ResourceId ID; uint64_t offset; uint64_t size; @@ -1056,8 +1058,9 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails, if(descriptorLen == UINT64_MAX) descriptorLen = len - descriptorBind->byteOffset; - tag = QVariant::fromValue(VulkanBufferTag(isrw, bindPoint, buf->resourceId, - descriptorBind->byteOffset, descriptorLen)); + tag = QVariant::fromValue(VulkanBufferTag(isrw, bindPoint, descriptorBind->viewFormat, + buf->resourceId, descriptorBind->byteOffset, + descriptorLen)); isbuf = true; } @@ -1973,7 +1976,7 @@ void VulkanPipelineStateViewer::setState() length, s.counterBufferResourceId, (qulonglong)s.counterBufferOffset, QString()}); node->setTag(QVariant::fromValue( - VulkanBufferTag(false, ~0U, s.bufferResourceId, s.byteOffset, length))); + VulkanBufferTag(false, ~0U, ResourceFormat(), s.bufferResourceId, s.byteOffset, length))); if(!filledSlot) setEmptyRow(node); @@ -2391,46 +2394,6 @@ void VulkanPipelineStateViewer::setState() } } -QString VulkanPipelineStateViewer::formatMembers(int indent, const QString &nameprefix, - const rdcarray &vars) -{ - QString indentstr(indent * 4, QLatin1Char(' ')); - - QString ret = QString(); - - int i = 0; - - for(const ShaderConstant &v : vars) - { - if(!v.type.members.isEmpty()) - { - if(i > 0) - ret += lit("\n"); - ret += indentstr + lit("// struct %1\n").arg(v.type.descriptor.name); - ret += indentstr + lit("{\n") + formatMembers(indent + 1, v.name + lit("_"), v.type.members) + - indentstr + lit("}\n"); - if(i < vars.count() - 1) - ret += lit("\n"); - } - else - { - QString arr = QString(); - if(v.type.descriptor.elements > 1) - arr = QFormatStr("[%1]").arg(v.type.descriptor.elements); - ret += QFormatStr("%1%2 %3%4%5;\n") - .arg(indentstr) - .arg(v.type.descriptor.name) - .arg(nameprefix) - .arg(v.name) - .arg(arr); - } - - i++; - } - - return ret; -} - void VulkanPipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, int column) { const VKPipe::Shader *stage = stageForSender(item->treeWidget()); @@ -2478,41 +2441,7 @@ void VulkanPipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, i ? stage->reflection->readWriteResources[buf.bindPoint] : stage->reflection->readOnlyResources[buf.bindPoint]; - format = lit("// struct %1\n").arg(shaderRes.variableType.descriptor.name); - - if(shaderRes.variableType.members.count() > 1) - { - format += lit("// members skipped as they are fixed size:\n"); - for(int i = 0; i < shaderRes.variableType.members.count() - 1; i++) - format += QFormatStr("// %1 %2;\n") - .arg(shaderRes.variableType.members[i].type.descriptor.name) - .arg(shaderRes.variableType.members[i].name); - } - - if(!shaderRes.variableType.members.isEmpty()) - { - format += lit("{\n") + formatMembers(1, QString(), shaderRes.variableType.members) + lit("}"); - } - else - { - const auto &desc = shaderRes.variableType.descriptor; - - format = QString(); - if(desc.rowMajorStorage) - format += lit("row_major "); - - format += ToQStr(desc.type); - if(desc.rows > 1 && desc.columns > 1) - format += QFormatStr("%1x%2").arg(desc.rows).arg(desc.columns); - else if(desc.columns > 1) - format += QString::number(desc.columns); - - if(!desc.name.isEmpty()) - format += lit(" ") + desc.name; - - if(desc.elements > 1) - format += QFormatStr("[%1]").arg(desc.elements); - } + format = m_Common.GenerateBufferFormatter(shaderRes, buf.fmt, buf.offset); } if(buf.ID != ResourceId()) diff --git a/renderdoc/api/replay/shader_types.h b/renderdoc/api/replay/shader_types.h index 038e05df7..9e60fcd63 100644 --- a/renderdoc/api/replay/shader_types.h +++ b/renderdoc/api/replay/shader_types.h @@ -636,19 +636,19 @@ struct ShaderVariableDescriptor return false; } DOCUMENT("The :class:`VarType` that this basic constant stores."); - VarType type; + VarType type = VarType::Unknown; DOCUMENT("The number of rows in this matrix."); - uint8_t rows; + uint8_t rows = 1; DOCUMENT("The number of columns in this matrix."); - uint8_t columns; + uint8_t columns = 1; DOCUMENT("The number of bytes between the start of one column/row in a matrix and the next."); - uint8_t matrixByteStride; + uint8_t matrixByteStride = 0; DOCUMENT("``True`` if the matrix is stored as row major instead of column major."); - bool rowMajorStorage; + bool rowMajorStorage = false; DOCUMENT("The number of elements in the array, or 1 if it's not an array."); - uint32_t elements; + uint32_t elements = 1; DOCUMENT("The number of bytes between the start of one element in the array and the next."); - uint32_t arrayByteStride; + uint32_t arrayByteStride = 0; DOCUMENT("The name of the type of this constant, e.g. a ``struct`` name."); rdcstr name; }; diff --git a/renderdoc/driver/gl/gl_shader_refl.cpp b/renderdoc/driver/gl/gl_shader_refl.cpp index cab0523e3..ca4d6e7cd 100644 --- a/renderdoc/driver/gl/gl_shader_refl.cpp +++ b/renderdoc/driver/gl/gl_shader_refl.cpp @@ -799,6 +799,8 @@ void ReconstructVarTree(GLenum query, GLuint sepProg, GLuint varIdx, GLint numPa bool multiDimArray = false; int arrayIdx = 0; + bool blockLevel = true; + // reverse figure out structures and structure arrays while(strchr(nm, '.') || strchr(nm, '[')) { @@ -856,6 +858,9 @@ void ReconstructVarTree(GLenum query, GLuint sepProg, GLuint varIdx, GLint numPa parentVar.type.descriptor.arrayByteStride = topLevelStride; parentVar.type.descriptor.matrixByteStride = 0; + if(!blockLevel) + topLevelStride = 0; + bool found = false; // if we can find the base variable already, we recurse into its members @@ -873,6 +878,8 @@ void ReconstructVarTree(GLenum query, GLuint sepProg, GLuint varIdx, GLint numPa parentmembers = &((*parentmembers)[i].type.members); found = true; + blockLevel = false; + break; } } @@ -1001,6 +1008,15 @@ void ReconstructVarTree(GLenum query, GLuint sepProg, GLuint varIdx, GLint numPa } } +void MakeChildByteOffsetsRelative(ShaderConstant &member) +{ + for(ShaderConstant &child : member.type.members) + { + MakeChildByteOffsetsRelative(child); + child.byteOffset -= member.byteOffset; + } +} + int ParseVersionStatement(const char *version) { if(strncmp(version, "#version", 8)) @@ -1633,57 +1649,29 @@ void MakeShaderReflection(GLenum shadType, GLuint sepProg, ShaderReflection &ref { sort(members[ssbo]); - // account for padding for std430 layout, if we have a root array of - // structs, we need to pad the struct up to have the correct alignment - if(members[ssbo].size() == 1 && !members[ssbo][0].type.members.empty() && - members[ssbo][0].type.descriptor.arrayByteStride != 0) + std::swap(rwresources[ssbos[ssbo]].variableType.members, members[ssbo][0].type.members); + } + + // patch-up reflection data. For top-level arrays use the stride & rough size to calculate the + // number of elements, and make all child byteOffset values relative to their parent + for(size_t ssbo = 0; ssbo < ssbos.size(); ssbo++) + { + rdcarray &ssboVars = rwresources[ssbo].variableType.members; + for(size_t rootMember = 0; rootMember + 1 < ssboVars.size(); rootMember++) { - // now that we're sorted, see what the tightly packed stride would be by looking at the last - // member - uint32_t desiredStride = members[ssbo][0].type.descriptor.arrayByteStride; + ShaderConstant &member = ssboVars[rootMember]; - ShaderConstant *last = &members[ssbo][0].type.members.back(); - while(!last->type.members.empty()) - last = &last->type.members.back(); + const uint32_t memberSizeBound = ssboVars[rootMember + 1].byteOffset - member.byteOffset; + const uint32_t stride = member.type.descriptor.arrayByteStride; - // start from the offset - uint32_t stride = last->byteOffset; - - // add its size - uint32_t size = last->type.descriptor.rows * last->type.descriptor.columns * 4; - if(last->type.descriptor.type == VarType::Double) - size *= 2; - - if(last->type.descriptor.elements > 1) - size *= last->type.descriptor.elements; - - stride += size; - - if(stride < desiredStride) + if(stride != 0 && member.type.descriptor.elements <= 1 && memberSizeBound > 2 * stride) { - uint32_t padding = desiredStride - stride; - - RDCASSERT((padding % 4) == 0 && padding <= 16, padding); - - padding /= 4; - - ShaderConstant paddingVar; - paddingVar.name = "__padding"; - paddingVar.byteOffset = last->byteOffset + size; - paddingVar.type.descriptor.type = VarType::UInt; - paddingVar.type.descriptor.rows = 1; - paddingVar.type.descriptor.columns = (uint8_t)RDCMIN(padding, 255U); - paddingVar.type.descriptor.elements = 1; - paddingVar.type.descriptor.rowMajorStorage = false; - paddingVar.type.descriptor.arrayByteStride = 0; - paddingVar.type.descriptor.matrixByteStride = 0; - paddingVar.type.descriptor.name = StringFormat::Fmt("uint%u", padding); - - members[ssbo][0].type.members.push_back(paddingVar); + member.type.descriptor.elements = memberSizeBound / stride; } } - std::swap(rwresources[ssbos[ssbo]].variableType.members, members[ssbo]); + for(ShaderConstant &member : ssboVars) + MakeChildByteOffsetsRelative(member); } delete[] members; diff --git a/util/test/demos/d3d11/d3d11_structured_buffer_nested.cpp b/util/test/demos/d3d11/d3d11_structured_buffer_nested.cpp new file mode 100644 index 000000000..36f380609 --- /dev/null +++ b/util/test/demos/d3d11/d3d11_structured_buffer_nested.cpp @@ -0,0 +1,140 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "d3d11_test.h" + +struct D3D11_Structured_Buffer_Nested : D3D11GraphicsTest +{ + static constexpr const char *Description = + "Test reading from structured buffers with nested structs"; + + std::string pixel = R"EOSHADER( + +struct supernest +{ + float x; +}; + +struct nest +{ + float3 v; + supernest s; + float a, b, c; +}; + +struct mystruct +{ + nest n[3]; + float4 p; +}; + +StructuredBuffer buf1 : register(t0); +Buffer buf2 : register(t1); +RWBuffer out_buf : register(u1); + +float4 main() : SV_Target0 +{ + int idx = 0; + out_buf[idx++] = buf1[0].p; + out_buf[idx++] = buf1[1].p; + out_buf[idx++] = buf1[2].p; + out_buf[idx++] = float4(buf1[0].n[0].v, 1.0f); + out_buf[idx++] = float4(buf1[3].n[1].v, 1.0f); + out_buf[idx++] = float4(buf1[6].n[2].v, 1.0f); + out_buf[idx++] = float4(buf1[4].n[0].a, 0.0f, 0.0f, 1.0f); + out_buf[idx++] = float4(buf1[5].n[1].b, 0.0f, 0.0f, 1.0f); + out_buf[idx++] = float4(buf1[7].n[2].c, 0.0f, 0.0f, 1.0f); + out_buf[idx++] = float4(buf1[8].n[1].s.x, 0.0f, 0.0f, 1.0f); + idx++; + out_buf[idx++] = float4(buf2[3], 1.0f); + out_buf[idx++] = float4(buf2[4], 1.0f); + out_buf[idx++] = float4(buf2[5], 1.0f); + return 1.0f.xxxx; +} + +)EOSHADER"; + + int main(int argc, char **argv) + { + // initialise, create window, create device, etc + if(!Init(argc, argv)) + return 3; + + ID3DBlobPtr vsblob = Compile(D3DDefaultVertex, "main", "vs_5_0"); + ID3DBlobPtr psblob = Compile(pixel, "main", "ps_5_0"); + + CreateDefaultInputLayout(vsblob); + + ID3D11VertexShaderPtr vs = CreateVS(vsblob); + ID3D11PixelShaderPtr ps = CreatePS(psblob); + + ID3D11BufferPtr vb = MakeBuffer().Vertex().Data(DefaultTri); + + float data[16 * 100]; + + for(int i = 0; i < 16 * 100; i++) + data[i] = float(i); + + ID3D11BufferPtr structbuf = MakeBuffer().Structured(25 * sizeof(float)).Data(data).SRV(); + ID3D11ShaderResourceViewPtr structbufSRV = MakeSRV(structbuf); + + ID3D11BufferPtr typedbuf = MakeBuffer().Data(data).SRV(); + ID3D11ShaderResourceViewPtr typedbufSRV = MakeSRV(typedbuf).Format(DXGI_FORMAT_R32G32B32_FLOAT); + + ID3D11BufferPtr outbuf = MakeBuffer().Structured(4 * sizeof(float)).Size(1024).UAV(); + ID3D11UnorderedAccessViewPtr outbufUAV = MakeUAV(outbuf); + + while(Running()) + { + ClearRenderTargetView(bbRTV, {0.4f, 0.5f, 0.6f, 1.0f}); + + IASetVertexBuffer(vb, sizeof(DefaultA2V), 0); + ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + ctx->IASetInputLayout(defaultLayout); + + ctx->VSSetShader(vs, NULL, 0); + ctx->PSSetShader(ps, NULL, 0); + + ID3D11ShaderResourceView *srvs[] = {structbufSRV, typedbufSRV}; + + ctx->PSSetShaderResources(0, 2, srvs); + + RSSetViewport({0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f}); + + float zeros[4] = {}; + ctx->ClearUnorderedAccessViewFloat(outbufUAV, zeros); + + ctx->OMSetRenderTargetsAndUnorderedAccessViews(1, &bbRTV.GetInterfacePtr(), NULL, 1, 1, + &outbufUAV.GetInterfacePtr(), NULL); + + ctx->Draw(3, 0); + + Present(); + } + + return 0; + } +}; + +REGISTER_TEST(D3D11_Structured_Buffer_Nested); \ No newline at end of file diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj index 5f38c57b6..0c619c7f4 100644 --- a/util/test/demos/demos.vcxproj +++ b/util/test/demos/demos.vcxproj @@ -141,6 +141,7 @@ + @@ -174,6 +175,7 @@ + true @@ -196,6 +198,7 @@ + diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters index 4f4d57f5c..2e6e7564a 100644 --- a/util/test/demos/demos.vcxproj.filters +++ b/util/test/demos/demos.vcxproj.filters @@ -255,6 +255,15 @@ Vulkan\demos + + D3D11\demos + + + Vulkan\demos + + + OpenGL\demos + diff --git a/util/test/demos/gl/gl_structured_buffer_nested.cpp b/util/test/demos/gl/gl_structured_buffer_nested.cpp new file mode 100644 index 000000000..6da9feb11 --- /dev/null +++ b/util/test/demos/gl/gl_structured_buffer_nested.cpp @@ -0,0 +1,212 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2015-2019 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "gl_test.h" + +struct GL_Structured_Buffer_Nested : OpenGLGraphicsTest +{ + static constexpr const char *Description = + "Just draws a simple triangle, using normal pipeline. Basic test that can be used " + "for any dead-simple tests that don't require any particular API use"; + + std::string common = R"EOSHADER( + +#version 430 core + +#define v2f v2f_block \ +{ \ + vec4 pos; \ + vec4 col; \ + vec4 uv; \ +} + +)EOSHADER"; + + std::string vertex = R"EOSHADER( + +layout(location = 0) in vec3 Position; +layout(location = 1) in vec4 Color; +layout(location = 2) in vec2 UV; + +out v2f vertOut; + +void main() +{ + vertOut.pos = vec4(Position.xyz, 1); + gl_Position = vertOut.pos; + vertOut.col = Color; + vertOut.uv = vec4(UV.xy, 0, 1); +} + +)EOSHADER"; + + std::string pixel = R"EOSHADER( + +in v2f vertIn; + +layout(location = 0, index = 0) out vec4 Color; + +struct supernest +{ + float x; +}; + +struct nest +{ + vec3 v; + supernest s; + float a, b, c; +}; + +layout(binding = 0, std430) buffer nest_struct_buffer +{ + nest n[3]; + vec4 p; + nest rtarray[]; +} nestbuf; + +layout(binding = 1) uniform samplerBuffer plainbuf; + +layout(binding = 2, std430) buffer struct_buffer +{ + nest rtarray[]; +} structbuf; + +layout(binding = 3, std430) buffer output_buffer +{ + vec4 dump[]; +} out_buf; + +void main() +{ + int idx = 0; + out_buf.dump[idx++] = vec4(nestbuf.n[0].v, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.n[1].a, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.n[2].c, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.n[2].s.x, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = nestbuf.p; + out_buf.dump[idx++] = vec4(nestbuf.rtarray[0].v, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.rtarray[3].v, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.rtarray[6].v, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.rtarray[4].a, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.rtarray[5].b, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.rtarray[7].c, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.rtarray[8].s.x, 0.0f, 0.0f, 1.0f); + idx++; + out_buf.dump[idx++] = texelFetch(plainbuf, 3); + out_buf.dump[idx++] = texelFetch(plainbuf, 4); + out_buf.dump[idx++] = texelFetch(plainbuf, 5); + idx++; + out_buf.dump[idx++] = vec4(structbuf.rtarray[0].v, 1.0f); + out_buf.dump[idx++] = vec4(structbuf.rtarray[3].v, 1.0f); + out_buf.dump[idx++] = vec4(structbuf.rtarray[6].v, 1.0f); + out_buf.dump[idx++] = vec4(structbuf.rtarray[4].a, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = vec4(structbuf.rtarray[5].b, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = vec4(structbuf.rtarray[7].c, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = vec4(structbuf.rtarray[8].s.x, 0.0f, 0.0f, 1.0f); + Color = vec4(1.0f, 1.0f, 1.0f, 1.0f); +} + +)EOSHADER"; + + int main(int argc, char **argv) + { + // initialise, create window, create context, etc + if(!Init(argc, argv)) + return 3; + + GLuint vao = MakeVAO(); + glBindVertexArray(vao); + + GLuint vb = MakeBuffer(); + glBindBuffer(GL_ARRAY_BUFFER, vb); + glBufferStorage(GL_ARRAY_BUFFER, sizeof(DefaultTri), DefaultTri, 0); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(DefaultA2V), (void *)(0)); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(DefaultA2V), (void *)(sizeof(Vec3f))); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(DefaultA2V), + (void *)(sizeof(Vec3f) + sizeof(Vec4f))); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + + GLuint program = MakeProgram(common + vertex, common + pixel); + glObjectLabel(GL_PROGRAM, program, -1, "Full program"); + + float data[16 * 100]; + + for(int i = 0; i < 16 * 100; i++) + data[i] = float(i); + + GLuint buf = MakeBuffer(); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, buf); + glBufferStorage(GL_SHADER_STORAGE_BUFFER, sizeof(data), data, 0); + + GLuint outbuf = MakeBuffer(); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, outbuf); + glBufferStorage(GL_SHADER_STORAGE_BUFFER, 1024, NULL, 0); + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buf); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, buf); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, outbuf); + + GLuint tbuf_store = MakeBuffer(); + glBindBuffer(GL_TEXTURE_BUFFER, tbuf_store); + glBufferStorage(GL_TEXTURE_BUFFER, sizeof(data), data, 0); + + GLuint tbuf = MakeTexture(); + glBindTexture(GL_TEXTURE_BUFFER, tbuf); + glTexBuffer(GL_TEXTURE_BUFFER, GL_RGB32F, tbuf_store); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_BUFFER, tbuf); + + while(Running()) + { + float zeros[4] = {}; + glBindBuffer(GL_SHADER_STORAGE_BUFFER, outbuf); + glClearBufferSubData(GL_SHADER_STORAGE_BUFFER, GL_RGBA32F, 0, 1024, GL_RGBA, GL_FLOAT, zeros); + + float col[] = {0.4f, 0.5f, 0.6f, 1.0f}; + glClearBufferfv(GL_COLOR, 0, col); + + glBindVertexArray(vao); + + glUseProgram(program); + + glViewport(0, 0, GLsizei(screenWidth), GLsizei(screenHeight)); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, buf); + + glDrawArrays(GL_TRIANGLES, 0, 3); + + Present(); + } + + return 0; + } +}; + +REGISTER_TEST(GL_Structured_Buffer_Nested); \ No newline at end of file diff --git a/util/test/demos/vk/vk_helpers.h b/util/test/demos/vk/vk_helpers.h index be234ae18..2759d45a1 100644 --- a/util/test/demos/vk/vk_helpers.h +++ b/util/test/demos/vk/vk_helpers.h @@ -487,6 +487,23 @@ struct ImageViewCreateInfo : public VkImageViewCreateInfo operator const VkImageViewCreateInfo *() const { return this; } }; +struct BufferViewCreateInfo : public VkBufferViewCreateInfo +{ + BufferViewCreateInfo(VkBuffer buffer, VkFormat format, VkDeviceSize offset = 0, + VkDeviceSize range = VK_WHOLE_SIZE) + { + sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO; + pNext = NULL; + this->flags = 0; + this->buffer = buffer; + this->format = format; + this->offset = offset; + this->range = range; + } + + operator const VkBufferViewCreateInfo *() const { return this; } +}; + struct BufferCreateInfo : public VkBufferCreateInfo { BufferCreateInfo(VkDeviceSize size, VkBufferUsageFlags usage, VkBufferCreateFlags flags = 0, diff --git a/util/test/demos/vk/vk_structured_buffer_nested.cpp b/util/test/demos/vk/vk_structured_buffer_nested.cpp new file mode 100644 index 000000000..1f9382472 --- /dev/null +++ b/util/test/demos/vk/vk_structured_buffer_nested.cpp @@ -0,0 +1,340 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "vk_test.h" + +struct VK_Structured_Buffer_Nested : VulkanGraphicsTest +{ + static constexpr const char *Description = + "Just draws a simple triangle, using normal pipeline. Basic test that can be used " + "for any dead-simple tests that don't require any particular API use"; + + std::string common = R"EOSHADER( + +#version 450 core + +struct v2f +{ + vec4 pos; + vec4 col; + vec4 uv; +}; + +)EOSHADER"; + + const std::string vertex = R"EOSHADER( + +layout(location = 0) in vec3 Position; +layout(location = 1) in vec4 Color; +layout(location = 2) in vec2 UV; + +layout(location = 0) out v2f vertOut; + +void main() +{ + vertOut.pos = vec4(Position.xyz*vec3(1,-1,1), 1); + gl_Position = vertOut.pos; + vertOut.col = Color; + vertOut.uv = vec4(UV.xy, 0, 1); +} + +)EOSHADER"; + + const std::string glslpixel = R"EOSHADER( + +layout(location = 0) in v2f vertIn; + +layout(location = 0, index = 0) out vec4 Color; + +struct supernest +{ + float x; +}; + +struct nest +{ + vec3 v; + supernest s; + float a, b, c; +}; + +layout(binding = 0, std430) buffer nest_struct_buffer +{ + nest n[3]; + vec4 p; + nest rtarray[]; +} nestbuf; + +layout(binding = 1) uniform samplerBuffer plainbuf; + +layout(binding = 2, std430) buffer struct_buffer +{ + nest rtarray[]; +} structbuf; + +layout(binding = 3, std430) buffer output_buffer +{ + vec4 dump[]; +} out_buf; + +void main() +{ + int idx = 0; + out_buf.dump[idx++] = vec4(nestbuf.n[0].v, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.n[1].a, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.n[2].c, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.n[2].s.x, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = nestbuf.p; + out_buf.dump[idx++] = vec4(nestbuf.rtarray[0].v, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.rtarray[3].v, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.rtarray[6].v, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.rtarray[4].a, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.rtarray[5].b, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.rtarray[7].c, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = vec4(nestbuf.rtarray[8].s.x, 0.0f, 0.0f, 1.0f); + idx++; + out_buf.dump[idx++] = texelFetch(plainbuf, 3); + out_buf.dump[idx++] = texelFetch(plainbuf, 4); + out_buf.dump[idx++] = texelFetch(plainbuf, 5); + idx++; + out_buf.dump[idx++] = vec4(structbuf.rtarray[0].v, 1.0f); + out_buf.dump[idx++] = vec4(structbuf.rtarray[3].v, 1.0f); + out_buf.dump[idx++] = vec4(structbuf.rtarray[6].v, 1.0f); + out_buf.dump[idx++] = vec4(structbuf.rtarray[4].a, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = vec4(structbuf.rtarray[5].b, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = vec4(structbuf.rtarray[7].c, 0.0f, 0.0f, 1.0f); + out_buf.dump[idx++] = vec4(structbuf.rtarray[8].s.x, 0.0f, 0.0f, 1.0f); + Color = vec4(1.0f, 1.0f, 1.0f, 1.0f); +} + +)EOSHADER"; + + std::string hlslpixel = R"EOSHADER( + +struct supernest +{ + float x; +}; + +struct nest +{ + float3 v; + supernest s; + float a, b, c; +}; + +struct mystruct +{ + nest n[3]; + float3 p; +}; + +StructuredBuffer buf1 : register(t0); +Buffer buf2 : register(t1); + +struct dump +{ + float4 val; +}; + +RWStructuredBuffer out_buf : register(u3); + +float4 main() : SV_Target0 +{ + int idx = 0; + out_buf[idx++].val = float4(buf1[0].p, 1.0f); + out_buf[idx++].val = float4(buf1[1].p, 1.0f); + out_buf[idx++].val = float4(buf1[2].p, 1.0f); + out_buf[idx++].val = float4(buf1[0].n[0].v, 1.0f); + out_buf[idx++].val = float4(buf1[3].n[1].v, 1.0f); + out_buf[idx++].val = float4(buf1[6].n[2].v, 1.0f); + out_buf[idx++].val = float4(buf1[4].n[0].a, 0.0f, 0.0f, 1.0f); + out_buf[idx++].val = float4(buf1[5].n[1].b, 0.0f, 0.0f, 1.0f); + out_buf[idx++].val = float4(buf1[7].n[2].c, 0.0f, 0.0f, 1.0f); + out_buf[idx++].val = float4(buf1[8].n[1].s.x, 0.0f, 0.0f, 1.0f); + idx++; + out_buf[idx++].val = float4(buf2[3], 1.0f); + out_buf[idx++].val = float4(buf2[4], 1.0f); + out_buf[idx++].val = float4(buf2[5], 1.0f); + return 1.0f.xxxx; +} + +)EOSHADER"; + + int main(int argc, char **argv) + { + features.fragmentStoresAndAtomics = true; + + // initialise, create window, create context, etc + if(!Init(argc, argv)) + return 3; + + VkDescriptorSetLayout setlayout = createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo({ + {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT}, + {1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT}, + {2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT}, + {3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT}, + })); + + VkPipelineLayout layout = createPipelineLayout(vkh::PipelineLayoutCreateInfo({setlayout})); + + vkh::GraphicsPipelineCreateInfo pipeCreateInfo; + + pipeCreateInfo.layout = layout; + pipeCreateInfo.renderPass = swapRenderPass; + + pipeCreateInfo.vertexInputState.vertexBindingDescriptions = {vkh::vertexBind(0, DefaultA2V)}; + pipeCreateInfo.vertexInputState.vertexAttributeDescriptions = { + vkh::vertexAttr(0, 0, DefaultA2V, pos), vkh::vertexAttr(1, 0, DefaultA2V, col), + vkh::vertexAttr(2, 0, DefaultA2V, uv), + }; + + pipeCreateInfo.stages = { + CompileShaderModule(common + vertex, ShaderLang::glsl, ShaderStage::vert, "main"), + CompileShaderModule(common + glslpixel, ShaderLang::glsl, ShaderStage::frag, "main"), + }; + + VkPipeline glslpipe = createGraphicsPipeline(pipeCreateInfo); + + pipeCreateInfo.stages[1] = + CompileShaderModule(hlslpixel, ShaderLang::hlsl, ShaderStage::frag, "main"); + + VkPipeline hlslpipe = createGraphicsPipeline(pipeCreateInfo); + + AllocatedBuffer vb( + allocator, vkh::BufferCreateInfo(sizeof(DefaultTri), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); + + vb.upload(DefaultTri); + + float data[16 * 100]; + + for(int i = 0; i < 16 * 100; i++) + data[i] = float(i); + + AllocatedBuffer ssbo( + allocator, vkh::BufferCreateInfo(sizeof(data), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); + + ssbo.upload(data); + + AllocatedBuffer out_ssbo(allocator, + vkh::BufferCreateInfo(1024, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + + VkDescriptorSet descset = allocateDescriptorSet(setlayout); + + VkBufferView bufview = + createBufferView(vkh::BufferViewCreateInfo(ssbo.buffer, VK_FORMAT_R32G32B32_SFLOAT)); + + vkh::updateDescriptorSets( + device, + { + vkh::WriteDescriptorSet(descset, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + {vkh::DescriptorBufferInfo(ssbo.buffer)}), + vkh::WriteDescriptorSet(descset, 1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, {bufview}), + vkh::WriteDescriptorSet(descset, 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + {vkh::DescriptorBufferInfo(ssbo.buffer)}), + vkh::WriteDescriptorSet(descset, 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + {vkh::DescriptorBufferInfo(out_ssbo.buffer)}), + }); + + while(Running()) + { + VkCommandBuffer cmd = GetCommandBuffer(); + + vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); + + VkImage swapimg = + StartUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + + vkCmdClearColorImage(cmd, swapimg, VK_IMAGE_LAYOUT_GENERAL, + vkh::ClearColorValue(0.4f, 0.5f, 0.6f, 1.0f), 1, + vkh::ImageSubresourceRange()); + + vkh::cmdPipelineBarrier( + cmd, {}, + { + vkh::BufferMemoryBarrier(VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, out_ssbo.buffer), + }); + + vkCmdFillBuffer(cmd, out_ssbo.buffer, 0, 1024, 0); + + vkh::cmdPipelineBarrier( + cmd, {}, + { + vkh::BufferMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + out_ssbo.buffer), + }); + + vkCmdBeginRenderPass( + cmd, vkh::RenderPassBeginInfo(swapRenderPass, swapFramebuffers[swapIndex], scissor), + VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, glslpipe); + vkh::cmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, {descset}, {}); + vkCmdSetViewport(cmd, 0, 1, &viewport); + vkCmdSetScissor(cmd, 0, 1, &scissor); + vkh::cmdBindVertexBuffers(cmd, 0, {vb.buffer}, {0}); + vkCmdDraw(cmd, 3, 1, 0, 0); + + vkCmdEndRenderPass(cmd); + + vkh::cmdPipelineBarrier( + cmd, {}, + { + vkh::BufferMemoryBarrier(VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + out_ssbo.buffer), + }); + + vkCmdBeginRenderPass( + cmd, vkh::RenderPassBeginInfo(swapRenderPass, swapFramebuffers[swapIndex], scissor), + VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, hlslpipe); + vkh::cmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, {descset}, {}); + vkCmdDraw(cmd, 3, 1, 0, 0); + + vkCmdEndRenderPass(cmd); + + FinishUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + + vkEndCommandBuffer(cmd); + + Submit(0, 1, {cmd}); + + Present(); + } + + return 0; + } +}; + +REGISTER_TEST(VK_Structured_Buffer_Nested); \ No newline at end of file diff --git a/util/test/demos/vk/vk_test.cpp b/util/test/demos/vk/vk_test.cpp index 66cf02507..a5805fb04 100644 --- a/util/test/demos/vk/vk_test.cpp +++ b/util/test/demos/vk/vk_test.cpp @@ -445,6 +445,9 @@ VulkanGraphicsTest::~VulkanGraphicsTest() for(VkImageView view : imageviews) vkDestroyImageView(device, view, NULL); + for(VkBufferView view : bufferviews) + vkDestroyBufferView(device, view, NULL); + for(VkPipelineLayout layout : pipelayouts) vkDestroyPipelineLayout(device, layout, NULL); @@ -748,6 +751,14 @@ VkImageView VulkanGraphicsTest::createImageView(const VkImageViewCreateInfo *inf return ret; } +VkBufferView VulkanGraphicsTest::createBufferView(const VkBufferViewCreateInfo *info) +{ + VkBufferView ret; + CHECK_VKR(vkCreateBufferView(device, info, NULL, &ret)); + bufferviews.push_back(ret); + return ret; +} + VkPipelineLayout VulkanGraphicsTest::createPipelineLayout(const VkPipelineLayoutCreateInfo *info) { VkPipelineLayout ret; diff --git a/util/test/demos/vk/vk_test.h b/util/test/demos/vk/vk_test.h index b5243459f..30e7989d1 100644 --- a/util/test/demos/vk/vk_test.h +++ b/util/test/demos/vk/vk_test.h @@ -172,6 +172,7 @@ struct VulkanGraphicsTest : public GraphicsTest VkFramebuffer createFramebuffer(const VkFramebufferCreateInfo *info); VkRenderPass createRenderPass(const VkRenderPassCreateInfo *info); VkImageView createImageView(const VkImageViewCreateInfo *info); + VkBufferView createBufferView(const VkBufferViewCreateInfo *info); VkPipelineLayout createPipelineLayout(const VkPipelineLayoutCreateInfo *info); VkDescriptorSetLayout createDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo *info); @@ -225,6 +226,7 @@ struct VulkanGraphicsTest : public GraphicsTest std::vector framebuffers; std::vector renderpasses; std::vector imageviews; + std::vector bufferviews; std::vector pipelayouts; std::vector setlayouts;