From fbb6b23b236d5820870b1beedbec60fc95257576 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 6 Feb 2019 13:18:32 +0000 Subject: [PATCH] Support advanced cbuffer layouts * This includes 8/16/64-bit integers, 16-bit/64-bit floats, and scalar block packing --- qrenderdoc/Code/FormatElement.cpp | 111 ++++- qrenderdoc/Code/pyrenderdoc/pyconversion.h | 61 +++ qrenderdoc/Code/pyrenderdoc/renderdoc.i | 4 + qrenderdoc/Windows/ShaderViewer.cpp | 10 +- renderdoc/api/replay/renderdoc_tostr.inl | 11 +- renderdoc/api/replay/replay_enums.h | 55 ++- renderdoc/api/replay/shader_types.h | 7 +- renderdoc/driver/d3d11/d3d11_shaderdebug.cpp | 2 +- renderdoc/driver/gl/gl_program_iterate.cpp | 6 +- renderdoc/driver/gl/gl_replay.cpp | 24 +- renderdoc/driver/gl/gl_shader_refl.cpp | 46 +- renderdoc/driver/shaders/dxbc/dxbc_debug.cpp | 16 +- .../driver/shaders/dxbc/dxbc_reflect.cpp | 3 +- renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp | 14 +- .../shaders/spirv/spirv_disassemble.cpp | 93 ++-- renderdoc/replay/replay_driver.cpp | 37 +- util/test/demos/demos.vcxproj | 1 + util/test/demos/demos.vcxproj.filters | 3 + util/test/demos/vk/vk_adv_cbuffer_zoo.cpp | 399 ++++++++++++++++++ util/test/demos/vk/vk_helpers.h | 17 + util/test/rdtest/testcase.py | 25 +- util/test/tests/Vulkan/VK_Adv_CBuffer_Zoo.py | 247 +++++++++++ 22 files changed, 1075 insertions(+), 117 deletions(-) create mode 100644 util/test/demos/vk/vk_adv_cbuffer_zoo.cpp create mode 100644 util/test/tests/Vulkan/VK_Adv_CBuffer_Zoo.py diff --git a/qrenderdoc/Code/FormatElement.cpp b/qrenderdoc/Code/FormatElement.cpp index 85916465c..1a525da02 100644 --- a/qrenderdoc/Code/FormatElement.cpp +++ b/qrenderdoc/Code/FormatElement.cpp @@ -158,9 +158,9 @@ QList FormatElement::ParseFormatString(const QString &formatStrin "|unormh|unormb" // UNORM 16-bit and 8-bit types "|snormh|snormb" // SNORM 16-bit and 8-bit types "|bool" // bool is stored as 4-byte int - "|byte|short|int" // signed ints - "|ubyte|ushort|uint" // unsigned ints - "|xbyte|xshort|xint" // hex ints + "|byte|short|int|long" // signed ints + "|ubyte|ushort|uint|ulong" // unsigned ints + "|xbyte|xshort|xint|xlong" // hex ints "|half|float|double" // float types "|vec|uvec|ivec" // OpenGL vector types "|mat|umat|imat" // OpenGL matrix types @@ -283,6 +283,16 @@ QList FormatElement::ParseFormatString(const QString &formatStrin type = CompType::UInt; width = 2; } + else if(basetype == lit("long")) + { + type = CompType::SInt; + width = 8; + } + else if(basetype == lit("ulong") || basetype == lit("xlong")) + { + type = CompType::UInt; + width = 8; + } else if(basetype == lit("int") || basetype == lit("ivec") || basetype == lit("imat")) { type = CompType::SInt; @@ -862,12 +872,51 @@ ShaderVariable FormatElement::GetShaderVar(const byte *&data, const byte *end) c ret.name = name.toUtf8().data(); ret.type = VarType::Float; if(format.compType == CompType::UInt) - ret.type = VarType::UInt; - if(format.compType == CompType::SInt) - ret.type = VarType::Int; - if(format.compType == CompType::Double) + { + if(format.compByteWidth == 8) + ret.type = VarType::ULong; + else if(format.compByteWidth == 4) + ret.type = VarType::UInt; + else if(format.compByteWidth == 2) + ret.type = VarType::UShort; + else if(format.compByteWidth == 1) + ret.type = VarType::UByte; + else + qCritical() << "Unexpeted component bytewidth for uint: " << format.compByteWidth; + } + else if(format.compType == CompType::SInt) + { + if(format.compByteWidth == 8) + ret.type = VarType::SLong; + else if(format.compByteWidth == 4) + ret.type = VarType::SInt; + else if(format.compByteWidth == 2) + ret.type = VarType::SShort; + else if(format.compByteWidth == 1) + ret.type = VarType::SByte; + else + qCritical() << "Unexpeted component bytewidth for sint: " << format.compByteWidth; + } + else if(format.compType == CompType::Double) + { ret.type = VarType::Double; + if(format.compByteWidth != 8) + qCritical() << "Unexpeted component bytewidth for double: " << format.compByteWidth; + } + else + { + // assume float/double + if(format.compByteWidth == 8) + ret.type = VarType::Double; + else if(format.compByteWidth == 4) + ret.type = VarType::Float; + else if(format.compByteWidth == 2) + ret.type = VarType::Half; + else + qCritical() << "Unexpeted component bytewidth for float: " << format.compByteWidth; + } + ret.columns = qMin(format.compCount, uint8_t(4)); ret.rows = qMin(matrixdim, 4U); @@ -892,9 +941,15 @@ ShaderVariable FormatElement::GetShaderVar(const byte *&data, const byte *end) c if(ret.type == VarType::Double) ret.value.dv[dst] = o.toDouble(); - else if(ret.type == VarType::UInt) + if(ret.type == VarType::Float || ret.type == VarType::Half) + ret.value.dv[dst] = o.toFloat(); + else if(ret.type == VarType::ULong) + ret.value.u64v[dst] = o.toULongLong(); + else if(ret.type == VarType::SLong) + ret.value.s64v[dst] = o.toLongLong(); + else if(ret.type == VarType::UInt || ret.type == VarType::UShort || ret.type == VarType::UByte) ret.value.uv[dst] = o.toUInt(); - else if(ret.type == VarType::Int) + else if(ret.type == VarType::SInt || ret.type == VarType::SShort || ret.type == VarType::SByte) ret.value.iv[dst] = o.toInt(); else ret.value.fv[dst] = o.toFloat(); @@ -930,8 +985,17 @@ QString TypeString(const ShaderVariable &v) QString typeStr = ToQStr(v.type); - if(v.displayAsHex && v.type == VarType::UInt) - typeStr = lit("xint"); + if(v.displayAsHex) + { + if(v.type == VarType::ULong) + typeStr = lit("xlong"); + else if(v.type == VarType::UInt) + typeStr = lit("xint"); + else if(v.type == VarType::UShort) + typeStr = lit("xshort"); + else if(v.type == VarType::UByte) + typeStr = lit("xbyte"); + } if(v.rows == 1 && v.columns == 1) return typeStr; @@ -975,11 +1039,19 @@ QString RowString(const ShaderVariable &v, uint32_t row, VarType type) return RowValuesToString((int)v.columns, v.value.dv[row * v.columns + 0], v.value.dv[row * v.columns + 1], v.value.dv[row * v.columns + 2], v.value.dv[row * v.columns + 3]); - else if(type == VarType::Int) + else if(type == VarType::SLong) + return RowValuesToString((int)v.columns, v.value.s64v[row * v.columns + 0], + v.value.s64v[row * v.columns + 1], v.value.s64v[row * v.columns + 2], + v.value.s64v[row * v.columns + 3]); + else if(type == VarType::ULong) + return RowValuesToString((int)v.columns, v.value.u64v[row * v.columns + 0], + v.value.u64v[row * v.columns + 1], v.value.u64v[row * v.columns + 2], + v.value.u64v[row * v.columns + 3]); + else if(type == VarType::SInt || type == VarType::SShort || type == VarType::SByte) return RowValuesToString((int)v.columns, v.value.iv[row * v.columns + 0], v.value.iv[row * v.columns + 1], v.value.iv[row * v.columns + 2], v.value.iv[row * v.columns + 3]); - else if(type == VarType::UInt) + else if(type == VarType::UInt || type == VarType::UShort || type == VarType::UByte) return RowValuesToString((int)v.columns, v.value.uv[row * v.columns + 0], v.value.uv[row * v.columns + 1], v.value.uv[row * v.columns + 2], v.value.uv[row * v.columns + 3]); @@ -1023,8 +1095,17 @@ QString RowTypeString(const ShaderVariable &v) QString typeStr = ToQStr(v.type); - if(v.displayAsHex && v.type == VarType::UInt) - typeStr = lit("xint"); + if(v.displayAsHex) + { + if(v.type == VarType::ULong) + typeStr = lit("xlong"); + else if(v.type == VarType::UInt) + typeStr = lit("xint"); + else if(v.type == VarType::UShort) + typeStr = lit("xshort"); + else if(v.type == VarType::UByte) + typeStr = lit("xbyte"); + } if(v.columns == 1) return typeStr; diff --git a/qrenderdoc/Code/pyrenderdoc/pyconversion.h b/qrenderdoc/Code/pyrenderdoc/pyconversion.h index 1053ff819..2d68a4d11 100644 --- a/qrenderdoc/Code/pyrenderdoc/pyconversion.h +++ b/qrenderdoc/Code/pyrenderdoc/pyconversion.h @@ -189,6 +189,27 @@ struct TypeConversion static PyObject *ConvertToPy(const uint8_t &in) { return PyLong_FromUnsignedLong(in); } }; +template <> +struct TypeConversion +{ + static int ConvertFromPy(PyObject *in, int8_t &out) + { + if(!PyLong_Check(in)) + return SWIG_TypeError; + + uint32_t longval = PyLong_AsUnsignedLong(in); + + if(PyErr_Occurred() || longval > 0xff) + return SWIG_OverflowError; + + out = int8_t(longval & 0xff); + + return SWIG_OK; + } + + static PyObject *ConvertToPy(const int8_t &in) { return PyLong_FromLong(in); } +}; + template <> struct TypeConversion { @@ -210,6 +231,27 @@ struct TypeConversion static PyObject *ConvertToPy(const uint16_t &in) { return PyLong_FromUnsignedLong(in); } }; +template <> +struct TypeConversion +{ + static int ConvertFromPy(PyObject *in, int16_t &out) + { + if(!PyLong_Check(in)) + return SWIG_TypeError; + + uint32_t longval = PyLong_AsLong(in); + + if(PyErr_Occurred() || longval > 0xffff) + return SWIG_OverflowError; + + out = int16_t(longval & 0xff); + + return SWIG_OK; + } + + static PyObject *ConvertToPy(const int16_t &in) { return PyLong_FromLong(in); } +}; + template <> struct TypeConversion { @@ -267,6 +309,25 @@ struct TypeConversion static PyObject *ConvertToPy(const uint64_t &in) { return PyLong_FromUnsignedLongLong(in); } }; +template <> +struct TypeConversion +{ + static int ConvertFromPy(PyObject *in, int64_t &out) + { + if(!PyLong_Check(in)) + return SWIG_TypeError; + + out = PyLong_AsLongLong(in); + + if(PyErr_Occurred()) + return SWIG_OverflowError; + + return SWIG_OK; + } + + static PyObject *ConvertToPy(const int64_t &in) { return PyLong_FromLongLong(in); } +}; + template <> struct TypeConversion { diff --git a/qrenderdoc/Code/pyrenderdoc/renderdoc.i b/qrenderdoc/Code/pyrenderdoc/renderdoc.i index 636479034..d28e0c975 100644 --- a/qrenderdoc/Code/pyrenderdoc/renderdoc.i +++ b/qrenderdoc/Code/pyrenderdoc/renderdoc.i @@ -155,9 +155,13 @@ FIXED_ARRAY_TYPEMAPS(double) FIXED_ARRAY_TYPEMAPS(float) FIXED_ARRAY_TYPEMAPS(bool) FIXED_ARRAY_TYPEMAPS(uint64_t) +FIXED_ARRAY_TYPEMAPS(int64_t) FIXED_ARRAY_TYPEMAPS(uint32_t) FIXED_ARRAY_TYPEMAPS(int32_t) FIXED_ARRAY_TYPEMAPS(uint16_t) +FIXED_ARRAY_TYPEMAPS(int16_t) +FIXED_ARRAY_TYPEMAPS(uint8_t) +FIXED_ARRAY_TYPEMAPS(int8_t) REFCOUNTED_TYPE(SDChunk); REFCOUNTED_TYPE(SDObject); diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index 55781a7b9..8044279ca 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -1483,8 +1483,8 @@ void ShaderViewer::runTo(int runToInstruction, bool forward, ShaderEvents condit QString ShaderViewer::stringRep(const ShaderVariable &var, bool useType) { - if(ui->intView->isChecked() || (useType && var.type == VarType::Int)) - return RowString(var, 0, VarType::Int); + if(ui->intView->isChecked() || (useType && var.type == VarType::SInt)) + return RowString(var, 0, VarType::SInt); if(useType && var.type == VarType::UInt) return RowString(var, 0, VarType::UInt); @@ -1974,7 +1974,7 @@ void ShaderViewer::updateDebugging() if(l.type == VarType::UInt) typeName = lit("uint"); - else if(l.type == VarType::Int) + else if(l.type == VarType::SInt) typeName = lit("int"); else if(l.type == VarType::Float) typeName = lit("float"); @@ -2047,7 +2047,7 @@ void ShaderViewer::updateDebugging() if(l.type == VarType::UInt) value += Formatter::Format(var->value.uv[r.component]); - else if(l.type == VarType::Int) + else if(l.type == VarType::SInt) value += Formatter::Format(var->value.iv[r.component]); else if(l.type == VarType::Float) value += Formatter::Format(var->value.fv[r.component]); @@ -3163,7 +3163,7 @@ void ShaderViewer::updateVariableTooltip() { if(l.type == VarType::UInt) tooltip += Formatter::Format(var->value.uv[r.component]); - else if(l.type == VarType::Int) + else if(l.type == VarType::SInt) tooltip += Formatter::Format(var->value.iv[r.component]); else if(l.type == VarType::Float) tooltip += Formatter::Format(var->value.fv[r.component]); diff --git a/renderdoc/api/replay/renderdoc_tostr.inl b/renderdoc/api/replay/renderdoc_tostr.inl index a114fe251..dc602ab10 100644 --- a/renderdoc/api/replay/renderdoc_tostr.inl +++ b/renderdoc/api/replay/renderdoc_tostr.inl @@ -716,9 +716,16 @@ std::string DoStringise(const VarType &el) BEGIN_ENUM_STRINGISE(VarType) { STRINGISE_ENUM_CLASS_NAMED(Float, "float"); - STRINGISE_ENUM_CLASS_NAMED(Int, "int"); - STRINGISE_ENUM_CLASS_NAMED(UInt, "uint"); STRINGISE_ENUM_CLASS_NAMED(Double, "double"); + STRINGISE_ENUM_CLASS_NAMED(Half, "half"); + STRINGISE_ENUM_CLASS_NAMED(SInt, "int"); + STRINGISE_ENUM_CLASS_NAMED(UInt, "uint"); + STRINGISE_ENUM_CLASS_NAMED(SShort, "short"); + STRINGISE_ENUM_CLASS_NAMED(UShort, "ushort"); + STRINGISE_ENUM_CLASS_NAMED(SLong, "long"); + STRINGISE_ENUM_CLASS_NAMED(ULong, "ulong"); + STRINGISE_ENUM_CLASS_NAMED(SByte, "byte"); + STRINGISE_ENUM_CLASS_NAMED(UByte, "ubyte"); STRINGISE_ENUM_CLASS_NAMED(Unknown, "unknown"); } END_ENUM_STRINGISE(); diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 82978f282..7d8dbf787 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -142,18 +142,46 @@ DOCUMENT(R"(Represents the base type of a shader variable in debugging or consta A single-precision (32-bit) floating point value. -.. data:: Int - - A signed integer value. - -.. data:: UInt - - An unsigned integer value. - .. data:: Double A double-precision (64-bit) floating point value. +.. data:: Half + + A half-precision (16-bit) floating point value. + +.. data:: SInt + + A signed 32-bit integer value. + +.. data:: UInt + + An unsigned 32-bit integer value. + +.. data:: SShort + + A signed 16-bit integer value. + +.. data:: UShort + + An unsigned 16-bit integer value. + +.. data:: SLong + + A signed 64-bit integer value. + +.. data:: ULong + + An unsigned 64-bit integer value. + +.. data:: SByte + + A signed 8-bit integer value. + +.. data:: UByte + + An unsigned 8-bit integer value. + .. data:: Unknown An unknown type. @@ -161,9 +189,16 @@ DOCUMENT(R"(Represents the base type of a shader variable in debugging or consta enum class VarType : uint32_t { Float = 0, - Int, - UInt, Double, + Half, + SInt, + UInt, + SShort, + UShort, + SLong, + ULong, + SByte, + UByte, Unknown = ~0U, }; diff --git a/renderdoc/api/replay/shader_types.h b/renderdoc/api/replay/shader_types.h index 9f3a4a80e..038e05df7 100644 --- a/renderdoc/api/replay/shader_types.h +++ b/renderdoc/api/replay/shader_types.h @@ -120,8 +120,11 @@ union ShaderValue DOCUMENT("``double`` values."); double dv[16]; - DOCUMENT("64-bit integer values."); + DOCUMENT("64-bit unsigned integer values."); uint64_t u64v[16]; + + DOCUMENT("64-bit signed integer values."); + int64_t s64v[16]; }; DOCUMENT(R"(Holds a single named shader variable. It contains either a primitive type (up to a 4x4 @@ -166,7 +169,7 @@ struct ShaderVariable displayAsHex = isStruct = rowMajor = false; for(int i = 0; i < 16; i++) value.uv[i] = 0; - type = VarType::Int; + type = VarType::SInt; value.i.x = x; value.i.y = y; value.i.z = z; diff --git a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp index 322ad3f8e..e51c3d16e 100644 --- a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp +++ b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp @@ -288,7 +288,7 @@ ShaderDebug::State D3D11DebugManager::CreateShaderDebugState(ShaderDebugTrace &t if(sig.compType == CompType::UInt) v.type = VarType::UInt; else if(sig.compType == CompType::SInt) - v.type = VarType::Int; + v.type = VarType::SInt; if(trace.inputs[sig.regIndex].columns == 0) trace.inputs[sig.regIndex] = v; diff --git a/renderdoc/driver/gl/gl_program_iterate.cpp b/renderdoc/driver/gl/gl_program_iterate.cpp index c47a2af9d..6233fed94 100644 --- a/renderdoc/driver/gl/gl_program_iterate.cpp +++ b/renderdoc/driver/gl/gl_program_iterate.cpp @@ -193,7 +193,7 @@ void DoSerialise(SerialiserType &ser, ProgramUniformValue &el) case eGL_INT: case eGL_INT_VEC2: case eGL_INT_VEC3: - case eGL_INT_VEC4: baseType = VarType::Int; break; + case eGL_INT_VEC4: baseType = VarType::SInt; break; case eGL_UNSIGNED_INT: case eGL_BOOL: case eGL_UNSIGNED_INT_VEC2: @@ -251,7 +251,7 @@ void DoSerialise(SerialiserType &ser, ProgramUniformValue &el) { if(baseType == VarType::Float) ser.Serialise("data", fv, elemCount, SerialiserFlags::NoFlags); - else if(baseType == VarType::Int) + else if(baseType == VarType::SInt) ser.Serialise("data", iv, elemCount, SerialiserFlags::NoFlags); else if(baseType == VarType::UInt) ser.Serialise("data", uv, elemCount, SerialiserFlags::NoFlags); @@ -264,7 +264,7 @@ void DoSerialise(SerialiserType &ser, ProgramUniformValue &el) ser.Serialise("data", fv, elemCount, SerialiserFlags::NoFlags); else if(baseType == VarType::Float) ser.Serialise("data", dv, elemCount, SerialiserFlags::NoFlags); - else if(baseType == VarType::Int) + else if(baseType == VarType::SInt) ser.Serialise("data", iv, elemCount, SerialiserFlags::NoFlags); else if(baseType == VarType::UInt) ser.Serialise("data", uv, elemCount, SerialiserFlags::NoFlags); diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index a936538b5..fd2b71612 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -2025,10 +2025,20 @@ void GLReplay::OpenGLFillCBufferVariables(GLuint prog, bool bufferBacked, std::s switch(var.type) { case VarType::Unknown: + case VarType::SLong: + case VarType::ULong: + case VarType::SShort: + case VarType::UShort: + case VarType::SByte: + case VarType::UByte: + case VarType::Half: + RDCERR("Unexpected base variable type %s, treating as float", + ToStr(var.type).c_str()); + // deliberate fall-through case VarType::Float: GL.glGetUniformfv(prog, location, (float *)uniformData.data()); break; - case VarType::Int: + case VarType::SInt: GL.glGetUniformiv(prog, location, (int32_t *)uniformData.data()); break; case VarType::UInt: @@ -2060,10 +2070,20 @@ void GLReplay::OpenGLFillCBufferVariables(GLuint prog, bool bufferBacked, std::s switch(var.type) { case VarType::Unknown: + case VarType::SLong: + case VarType::ULong: + case VarType::SShort: + case VarType::UShort: + case VarType::SByte: + case VarType::UByte: + case VarType::Half: + RDCERR("Unexpected base variable type %s, treating as float", + ToStr(var.type).c_str()); + // deliberate fall-through case VarType::Float: GL.glGetUniformfv(prog, location + a, (float *)uniformData.data()); break; - case VarType::Int: + case VarType::SInt: GL.glGetUniformiv(prog, location + a, (int32_t *)uniformData.data()); break; case VarType::UInt: diff --git a/renderdoc/driver/gl/gl_shader_refl.cpp b/renderdoc/driver/gl/gl_shader_refl.cpp index 4dd5882a5..903dfebff 100644 --- a/renderdoc/driver/gl/gl_shader_refl.cpp +++ b/renderdoc/driver/gl/gl_shader_refl.cpp @@ -575,7 +575,7 @@ void ReconstructVarTree(GLenum query, GLuint sepProg, GLuint varIdx, GLint numPa case eGL_INT_VEC4: case eGL_INT_VEC3: case eGL_INT_VEC2: - case eGL_INT: var.type.descriptor.type = VarType::Int; break; + case eGL_INT: var.type.descriptor.type = VarType::SInt; break; default: // not a variable (sampler etc) return; @@ -1163,67 +1163,67 @@ void MakeShaderReflection(GLenum shadType, GLuint sepProg, ShaderReflection &ref { res.resType = TextureType::Buffer; res.variableType.descriptor.name = "isamplerBuffer"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; } else if(values[0] == eGL_INT_SAMPLER_1D) { res.resType = TextureType::Texture1D; res.variableType.descriptor.name = "isampler1D"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; } else if(values[0] == eGL_INT_SAMPLER_1D_ARRAY) { res.resType = TextureType::Texture1DArray; res.variableType.descriptor.name = "isampler1DArray"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; } else if(values[0] == eGL_INT_SAMPLER_2D) { res.resType = TextureType::Texture2D; res.variableType.descriptor.name = "isampler2D"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; } else if(values[0] == eGL_INT_SAMPLER_2D_ARRAY) { res.resType = TextureType::Texture2DArray; res.variableType.descriptor.name = "isampler2DArray"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; } else if(values[0] == eGL_INT_SAMPLER_2D_RECT) { res.resType = TextureType::TextureRect; res.variableType.descriptor.name = "isampler2DRect"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; } else if(values[0] == eGL_INT_SAMPLER_3D) { res.resType = TextureType::Texture3D; res.variableType.descriptor.name = "isampler3D"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; } else if(values[0] == eGL_INT_SAMPLER_CUBE) { res.resType = TextureType::TextureCube; res.variableType.descriptor.name = "isamplerCube"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; } else if(values[0] == eGL_INT_SAMPLER_CUBE_MAP_ARRAY) { res.resType = TextureType::TextureCubeArray; res.variableType.descriptor.name = "isamplerCubeArray"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; } else if(values[0] == eGL_INT_SAMPLER_2D_MULTISAMPLE) { res.resType = TextureType::Texture2DMS; res.variableType.descriptor.name = "isampler2DMS"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; } else if(values[0] == eGL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY) { res.resType = TextureType::Texture2DMSArray; res.variableType.descriptor.name = "isampler2DMSArray"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; } // unsigned int samplers else if(values[0] == eGL_UNSIGNED_INT_SAMPLER_BUFFER) @@ -1375,77 +1375,77 @@ void MakeShaderReflection(GLenum shadType, GLuint sepProg, ShaderReflection &ref { res.resType = TextureType::Buffer; res.variableType.descriptor.name = "iimageBuffer"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; res.isReadOnly = false; } else if(values[0] == eGL_INT_IMAGE_1D) { res.resType = TextureType::Texture1D; res.variableType.descriptor.name = "iimage1D"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; res.isReadOnly = false; } else if(values[0] == eGL_INT_IMAGE_1D_ARRAY) { res.resType = TextureType::Texture1DArray; res.variableType.descriptor.name = "iimage1DArray"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; res.isReadOnly = false; } else if(values[0] == eGL_INT_IMAGE_2D) { res.resType = TextureType::Texture2D; res.variableType.descriptor.name = "iimage2D"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; res.isReadOnly = false; } else if(values[0] == eGL_INT_IMAGE_2D_ARRAY) { res.resType = TextureType::Texture2DArray; res.variableType.descriptor.name = "iimage2DArray"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; res.isReadOnly = false; } else if(values[0] == eGL_INT_IMAGE_2D_RECT) { res.resType = TextureType::TextureRect; res.variableType.descriptor.name = "iimage2DRect"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; res.isReadOnly = false; } else if(values[0] == eGL_INT_IMAGE_3D) { res.resType = TextureType::Texture3D; res.variableType.descriptor.name = "iimage3D"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; res.isReadOnly = false; } else if(values[0] == eGL_INT_IMAGE_CUBE) { res.resType = TextureType::TextureCube; res.variableType.descriptor.name = "iimageCube"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; res.isReadOnly = false; } else if(values[0] == eGL_INT_IMAGE_CUBE_MAP_ARRAY) { res.resType = TextureType::TextureCubeArray; res.variableType.descriptor.name = "iimageCubeArray"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; res.isReadOnly = false; } else if(values[0] == eGL_INT_IMAGE_2D_MULTISAMPLE) { res.resType = TextureType::Texture2DMS; res.variableType.descriptor.name = "iimage2DMS"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; res.isReadOnly = false; } else if(values[0] == eGL_INT_IMAGE_2D_MULTISAMPLE_ARRAY) { res.resType = TextureType::Texture2DMSArray; res.variableType.descriptor.name = "iimage2DMSArray"; - res.variableType.descriptor.type = VarType::Int; + res.variableType.descriptor.type = VarType::SInt; res.isReadOnly = false; } // unsigned int images diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp index 9afe6502a..4577357aa 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp @@ -154,14 +154,14 @@ VarType State::OperationType(const OpcodeType &op) const case OPCODE_BREAKC: case OPCODE_IF: case OPCODE_ITOF: - case OPCODE_DTOI: return VarType::Int; + case OPCODE_DTOI: return VarType::SInt; case OPCODE_ATOMIC_IADD: case OPCODE_ATOMIC_IMAX: case OPCODE_ATOMIC_IMIN: case OPCODE_IMM_ATOMIC_IADD: case OPCODE_IMM_ATOMIC_IMAX: - case OPCODE_IMM_ATOMIC_IMIN: return VarType::Int; + case OPCODE_IMM_ATOMIC_IMIN: return VarType::SInt; case OPCODE_ATOMIC_AND: case OPCODE_ATOMIC_OR: case OPCODE_ATOMIC_XOR: @@ -243,7 +243,7 @@ ShaderVariable sat(const ShaderVariable &v, const VarType type) switch(type) { - case VarType::Int: + case VarType::SInt: { for(size_t i = 0; i < v.columns; i++) r.value.iv[i] = v.value.iv[i] < 0 ? 0 : (v.value.iv[i] > 1 ? 1 : v.value.iv[i]); @@ -292,7 +292,7 @@ ShaderVariable abs(const ShaderVariable &v, const VarType type) switch(type) { - case VarType::Int: + case VarType::SInt: { for(size_t i = 0; i < v.columns; i++) r.value.iv[i] = v.value.iv[i] > 0 ? v.value.iv[i] : -v.value.iv[i]; @@ -337,7 +337,7 @@ ShaderVariable neg(const ShaderVariable &v, const VarType type) switch(type) { - case VarType::Int: + case VarType::SInt: { for(size_t i = 0; i < v.columns; i++) r.value.iv[i] = -v.value.iv[i]; @@ -382,7 +382,7 @@ ShaderVariable mul(const ShaderVariable &a, const ShaderVariable &b, const VarTy switch(type) { - case VarType::Int: + case VarType::SInt: { for(size_t i = 0; i < a.columns; i++) r.value.iv[i] = a.value.iv[i] * b.value.iv[i]; @@ -432,7 +432,7 @@ ShaderVariable div(const ShaderVariable &a, const ShaderVariable &b, const VarTy switch(type) { - case VarType::Int: + case VarType::SInt: { for(size_t i = 0; i < a.columns; i++) r.value.iv[i] = a.value.iv[i] / b.value.iv[i]; @@ -482,7 +482,7 @@ ShaderVariable add(const ShaderVariable &a, const ShaderVariable &b, const VarTy switch(type) { - case VarType::Int: + case VarType::SInt: { for(size_t i = 0; i < a.columns; i++) r.value.iv[i] = a.value.iv[i] + b.value.iv[i]; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_reflect.cpp b/renderdoc/driver/shaders/dxbc/dxbc_reflect.cpp index 3c35b3c5f..93134bd82 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_reflect.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_reflect.cpp @@ -35,9 +35,10 @@ static ShaderVariableType MakeShaderVariableType(DXBC::CBufferVariableType type) switch(type.descriptor.type) { + // D3D treats all cbuffer variables as 32-bit regardless of declaration case DXBC::VARTYPE_MIN12INT: case DXBC::VARTYPE_MIN16INT: - case DXBC::VARTYPE_INT: ret.descriptor.type = VarType::Int; break; + case DXBC::VARTYPE_INT: ret.descriptor.type = VarType::SInt; break; case DXBC::VARTYPE_BOOL: case DXBC::VARTYPE_MIN16UINT: case DXBC::VARTYPE_UINT: ret.descriptor.type = VarType::UInt; break; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp b/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp index ca39383ee..ee9fffe7d 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp @@ -196,12 +196,14 @@ SPDBChunk::SPDBChunk(DXBCFile *dxbc, void *chunk) std::map typeInfo; // prepopulate with basic types - typeInfo[T_INT4] = {"int32_t", VarType::Int, 4, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_INT2] = {"int16_t", VarType::Int, 2, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_INT1] = {"int8_t", VarType::Int, 1, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_LONG] = {"int32_t", VarType::Int, 4, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_SHORT] = {"int16_t", VarType::Int, 2, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_CHAR] = {"char", VarType::Int, 1, 1, 0, LF_NUMERIC, {}}; + // for now we stick to full-precision 32-bit VarTypes. It's not clear if HLSL even emits the other + // types + typeInfo[T_INT4] = {"int32_t", VarType::SInt, 4, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_INT2] = {"int16_t", VarType::SInt, 2, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_INT1] = {"int8_t", VarType::SInt, 1, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_LONG] = {"int32_t", VarType::SInt, 4, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_SHORT] = {"int16_t", VarType::SInt, 2, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_CHAR] = {"char", VarType::SInt, 1, 1, 0, LF_NUMERIC, {}}; typeInfo[T_BOOL32FF] = {"bool", VarType::UInt, 4, 1, 0, LF_NUMERIC, {}}; typeInfo[T_UINT4] = {"uint32_t", VarType::UInt, 4, 1, 0, LF_NUMERIC, {}}; typeInfo[T_UINT2] = {"uint16_t", VarType::UInt, 2, 1, 0, LF_NUMERIC, {}}; diff --git a/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp b/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp index fb0eea152..7ed483a8a 100644 --- a/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp @@ -606,6 +606,62 @@ struct SPVTypeData return name; } + VarType GetVarType() + { + if(type == SPVTypeData::eFloat) + { + if(bitCount == 64) + return VarType::Double; + else if(bitCount == 32) + return VarType::Float; + else if(bitCount == 16) + return VarType::Half; + + RDCERR("Unexpected float bitcount: %u", bitCount); + + return VarType::Float; + } + else if(type == SPVTypeData::eBool) + { + // we treat bools as VkBool32 externally + return VarType::UInt; + } + else if(type == SPVTypeData::eUInt) + { + if(bitCount == 64) + return VarType::ULong; + else if(bitCount == 32) + return VarType::UInt; + else if(bitCount == 16) + return VarType::UShort; + else if(bitCount == 8) + return VarType::UByte; + + RDCERR("Unexpected uint bitcount: %u", bitCount); + + return VarType::UInt; + } + else if(type == SPVTypeData::eSInt) + { + if(bitCount == 64) + return VarType::SLong; + else if(bitCount == 32) + return VarType::SInt; + else if(bitCount == 16) + return VarType::SShort; + else if(bitCount == 8) + return VarType::SByte; + + RDCERR("Unexpected sint bitcount: %u", bitCount); + + return VarType::SInt; + } + + RDCERR("Unexpected base type variable %u", type); + + return VarType::Unknown; + } + vector *decorations; // struct/function @@ -3565,14 +3621,7 @@ void MakeConstantBlockVariable(ShaderConstant &outConst, SPVTypeData *type, cons if(type->type == SPVTypeData::eVector || type->type == SPVTypeData::eMatrix) { - if(type->baseType->type == SPVTypeData::eFloat) - outConst.type.descriptor.type = VarType::Float; - else if(type->baseType->type == SPVTypeData::eUInt || type->baseType->type == SPVTypeData::eBool) - outConst.type.descriptor.type = VarType::UInt; - else if(type->baseType->type == SPVTypeData::eSInt) - outConst.type.descriptor.type = VarType::Int; - else - RDCERR("Unexpected base type of constant variable %u", type->baseType->type); + outConst.type.descriptor.type = type->baseType->GetVarType(); outConst.type.descriptor.rowMajorStorage = (type->type == SPVTypeData::eVector); @@ -3599,15 +3648,7 @@ void MakeConstantBlockVariable(ShaderConstant &outConst, SPVTypeData *type, cons } else if(type->IsScalar()) { - if(type->type == SPVTypeData::eFloat) - outConst.type.descriptor.type = VarType::Float; - else if(type->type == SPVTypeData::eUInt || type->type == SPVTypeData::eBool) - outConst.type.descriptor.type = VarType::UInt; - else if(type->type == SPVTypeData::eSInt) - outConst.type.descriptor.type = VarType::Int; - else - RDCERR("Unexpected base type of constant variable %u", type->type); - + outConst.type.descriptor.type = type->GetVarType(); outConst.type.descriptor.rowMajorStorage = true; outConst.type.descriptor.rows = 1; outConst.type.descriptor.columns = 1; @@ -4416,14 +4457,7 @@ void SPVModule::MakeReflection(GraphicsAPI sourceAPI, ShaderStage stage, const s { res.resType = TextureType::Texture2D; - if(sampledType->type == SPVTypeData::eFloat) - res.variableType.descriptor.type = VarType::Float; - else if(sampledType->type == SPVTypeData::eUInt) - res.variableType.descriptor.type = VarType::UInt; - else if(sampledType->type == SPVTypeData::eSInt) - res.variableType.descriptor.type = VarType::Int; - else - RDCERR("Unexpected base type of resource %u", sampledType->type); + res.variableType.descriptor.type = sampledType->GetVarType(); } else { @@ -4441,14 +4475,7 @@ void SPVModule::MakeReflection(GraphicsAPI sourceAPI, ShaderStage stage, const s res.isReadOnly = !isrw; - if(sampledType->type == SPVTypeData::eFloat) - res.variableType.descriptor.type = VarType::Float; - else if(sampledType->type == SPVTypeData::eUInt) - res.variableType.descriptor.type = VarType::UInt; - else if(sampledType->type == SPVTypeData::eSInt) - res.variableType.descriptor.type = VarType::Int; - else - RDCERR("Unexpected base type of resource %u", sampledType->type); + res.variableType.descriptor.type = sampledType->GetVarType(); } res.variableType.descriptor.rows = 1; diff --git a/renderdoc/replay/replay_driver.cpp b/renderdoc/replay/replay_driver.cpp index cf34a4453..a33a8ca69 100644 --- a/renderdoc/replay/replay_driver.cpp +++ b/renderdoc/replay/replay_driver.cpp @@ -267,8 +267,12 @@ void StandardFillCBufferVariable(uint32_t dataOffset, const bytebuf &data, Shade const uint32_t cols = outvar.columns; size_t elemByteSize = 4; - if(type == VarType::Double) + if(type == VarType::Double || type == VarType::ULong || type == VarType::SLong) elemByteSize = 8; + else if(type == VarType::Half || type == VarType::UShort || type == VarType::SShort) + elemByteSize = 2; + else if(type == VarType::UByte || type == VarType::SByte) + elemByteSize = 1; // primary is the 'major' direction // so a matrix is a secondaryDim number of primaryDim-sized vectors @@ -307,7 +311,7 @@ void StandardFillCBufferVariable(uint32_t dataOffset, const bytebuf &data, Shade { ShaderVariable tmp = outvar; - if(type == VarType::Double) + if(elemByteSize == 8) { for(size_t ri = 0; ri < rows; ri++) for(size_t ci = 0; ci < cols; ci++) @@ -320,6 +324,35 @@ void StandardFillCBufferVariable(uint32_t dataOffset, const bytebuf &data, Shade outvar.value.uv[ri * cols + ci] = tmp.value.uv[ci * rows + ri]; } } + + // special case - decode halfs in-place, sign extend signed < 4 byte integers + if(type == VarType::Half) + { + for(size_t ri = 0; ri < rows; ri++) + { + for(size_t ci = 0; ci < cols; ci++) + { + outvar.value.fv[ri * cols + ci] = + ConvertFromHalf((uint16_t)outvar.value.uv[ri * cols + ci]); + } + } + } + else if(type == VarType::SShort || type == VarType::SByte) + { + const uint32_t testMask = (type == VarType::SShort ? 0x8000 : 0x80); + const uint32_t extendMask = (type == VarType::SShort ? 0xffff0000 : 0xffffff00); + + for(size_t ri = 0; ri < rows; ri++) + { + for(size_t ci = 0; ci < cols; ci++) + { + uint32_t &u = outvar.value.uv[ri * cols + ci]; + + if(u & testMask) + u |= extendMask; + } + } + } } } diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj index 3ff7a167e..5f38c57b6 100644 --- a/util/test/demos/demos.vcxproj +++ b/util/test/demos/demos.vcxproj @@ -198,6 +198,7 @@ + diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters index 632f0c4b2..4f4d57f5c 100644 --- a/util/test/demos/demos.vcxproj.filters +++ b/util/test/demos/demos.vcxproj.filters @@ -252,6 +252,9 @@ Vulkan\demos + + Vulkan\demos + diff --git a/util/test/demos/vk/vk_adv_cbuffer_zoo.cpp b/util/test/demos/vk/vk_adv_cbuffer_zoo.cpp new file mode 100644 index 000000000..3989bcfdf --- /dev/null +++ b/util/test/demos/vk/vk_adv_cbuffer_zoo.cpp @@ -0,0 +1,399 @@ +/****************************************************************************** + * 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 vec2 +{ + float x, y; +}; +struct vec3 +{ + float x, y, z; +}; +struct i8vec4 +{ + int8_t x, y, z, w; +}; +struct i8vec2 +{ + int8_t x, y; +}; +struct i16vec4 +{ + int16_t x, y, z, w; +}; +struct i16vec3 +{ + int16_t x, y, z; +}; +struct i16vec2 +{ + int16_t x, y; +}; +struct mat2x3 +{ + float m[2 * 3]; +}; + +// Block memory layout +struct S +{ + float a; + vec2 b; + double c; + float d; + vec3 e; + float f; +}; + +struct S8 +{ + int8_t a; + i8vec4 b; + i8vec2 c[4]; +}; + +struct S16 +{ + uint16_t a; + i16vec4 b; + i16vec2 c[4]; + int8_t d; +}; + +struct UBO +{ + float a; + vec2 b; + vec3 c; + float d[2]; + mat2x3 e; + mat2x3 f[2]; + float g; + S h; + S i[2]; + // i8vec4 pad1; + int8_t j; + S8 k; + S8 l[2]; + int8_t m; + S16 n; + uint8_t o; + S16 p[2]; + uint64_t q; + int64_t r; + uint16_t s; + int8_t test; +}; + +struct VK_Adv_CBuffer_Zoo : VulkanGraphicsTest +{ + static constexpr const char *Description = + "Tests VK_EXT_scalar_block_layout as well as 8-bit/16-bit storage " + "to ensure we correctly handle all types of offset and type."; + + std::string common = R"EOSHADER( + +#version 460 core + +#extension GL_EXT_scalar_block_layout : require +#extension GL_EXT_shader_16bit_storage : require +#extension GL_EXT_shader_8bit_storage : require +#extension GL_ARB_gpu_shader_int64 : require +#extension GL_EXT_shader_explicit_arithmetic_types : require +#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require + +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; + +// Block memory layout +struct S +{ + float a; // offset 0 + vec2 b; // offset 4 + double c; // offset 16 + float d; // offset 24 + vec3 e; // offset 28 + float f; // offset 40 + // size = 44, align = 8 +}; + +struct S8 +{ + int8_t a; // offset 0 + i8vec4 b; // offset 1 + i8vec2 c[4]; // offset 5 + // size = 13, align = 1 +}; + +struct S16 +{ + uint16_t a; // offset 0 + i16vec4 b; // offset 2 + i16vec2 c[4]; // offset 10 + int8_t d; // offset 26 + // size = 27, align = 2 +}; + +layout(column_major, scalar) uniform B1 +{ + float a; // offset = 0 + vec2 b; // offset = 4 + vec3 c; // offset = 12 + float d[2]; // offset = 24 + mat2x3 e; // offset = 32, takes 24 bytes, matrixstride = 12 + mat2x3 f[2]; // offset = 56, takes 48 bytes, matrixstride = 12, arraystride = 24 + float g; // offset = 104 + S h; // offset = 112 (aligned to multiple of 8) + S i[2]; // offset = 160 (aligned to multiple of 8) stride = 48 + i8vec4 pad1; // offset = 252 C pads after array here - not required in GLSL scalar packing + int8_t j; // offset = 256 + S8 k; // offset = 257 (aligned to multiple of 1) + S8 l[2]; // offset = 270 (aligned to multiple of 1) stride = 13 + int8_t m; // offset = 296 + S16 n; // offset = 298 (aligned to multiple of 2) + int8_t pad2; // offset = 325 C pads after struct here - not required in GLSL scalar packing + uint8_t o; // offset = 326 + S16 p[2]; // offset = 328 (aligned to multiple of 2) stride = 28 + int8_t pad3; // offset = 383 C pads after struct here - not required in GLSL scalar packing + uint64_t q; // offset = 384 + int64_t r; // offset = 392 + float16_t s; // offset = 400 + int8_t test; // offset = 402 +}; + +void main() +{ + vertOut.pos = vec4(Position.xyz*vec3(1,-1,1), 1); + gl_Position = vertOut.pos; + vertOut.uv = vec4(UV.xy, 0, 1); + + vertOut.col = vec4(1,0,0,0); + + if(int(test) == 42) + vertOut.col = vec4(0,1,0,0); +} + +)EOSHADER"; + + const std::string pixel = R"EOSHADER( + +layout(location = 0) in v2f vertIn; + +layout(location = 0, index = 0) out vec4 Color; + +void main() +{ + Color = vertIn.col; +} + +)EOSHADER"; + + int main(int argc, char **argv) + { + instExts.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + devExts.push_back(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME); + devExts.push_back(VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME); + devExts.push_back(VK_KHR_16BIT_STORAGE_EXTENSION_NAME); + devExts.push_back(VK_KHR_8BIT_STORAGE_EXTENSION_NAME); + + VkPhysicalDevice16BitStorageFeaturesKHR _16bitFeatures = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR, + }; + + VkPhysicalDevice16BitStorageFeaturesKHR _8bitFeatures = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR, + }; + + _16bitFeatures.uniformAndStorageBuffer16BitAccess = VK_TRUE; + _8bitFeatures.uniformAndStorageBuffer16BitAccess = VK_TRUE; + + devInfoNext = &_8bitFeatures; + _8bitFeatures.pNext = &_16bitFeatures; + + features.shaderFloat64 = true; + features.shaderInt16 = true; + features.shaderInt64 = true; + + // initialise, create window, create context, etc + if(!Init(argc, argv)) + return 3; + + _16bitFeatures.uniformAndStorageBuffer16BitAccess = VK_FALSE; + _8bitFeatures.uniformAndStorageBuffer16BitAccess = VK_FALSE; + + vkGetPhysicalDeviceFeatures2KHR(phys, vkh::PhysicalDeviceFeatures2KHR().next(&_16bitFeatures)); + vkGetPhysicalDeviceFeatures2KHR(phys, vkh::PhysicalDeviceFeatures2KHR().next(&_8bitFeatures)); + + // a bit late, but... + TEST_ASSERT(_16bitFeatures.uniformAndStorageBuffer16BitAccess && + _8bitFeatures.uniformAndStorageBuffer16BitAccess, + "8-bit or 16-bit uniform storage not available"); + + VkDescriptorSetLayout setlayout = createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo({ + {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_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 + pixel, ShaderLang::glsl, ShaderStage::frag, "main"), + }; + + VkPipeline pipe = 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); + + UBO cbufferdata = {}; + + // this value is checked by the shader to ensure everything has aligned just so. + cbufferdata.test = 42; + + // set some other values that we can inspect ourselves manually + cbufferdata.a = 1.0f; + cbufferdata.b.x = 2.0f; + cbufferdata.c.y = 3.0f; + cbufferdata.d[0] = 4.0f; + cbufferdata.d[1] = 5.0f; + cbufferdata.e.m[0] = 6.0f; + cbufferdata.e.m[1] = 7.0f; + cbufferdata.e.m[3] = 999.0f; + cbufferdata.f[0].m[0] = 8.0f; + cbufferdata.f[0].m[1] = 9.0f; + cbufferdata.f[0].m[3] = 999.0f; + cbufferdata.f[1].m[0] = 10.0f; + cbufferdata.f[1].m[1] = 11.0f; + cbufferdata.f[1].m[3] = 999.0f; + cbufferdata.g = 12.0f; + cbufferdata.h.c = 13.0; + cbufferdata.h.d = 14.0f; + cbufferdata.i[0].c = 15.0; + cbufferdata.i[1].d = 16.0f; + cbufferdata.j = 17; + cbufferdata.k.c[1].y = 18; + cbufferdata.l[0].a = 19; + cbufferdata.l[0].c[1].y = 20; + cbufferdata.l[1].a = 21; + cbufferdata.l[1].c[0].y = 22; + cbufferdata.m = -23; + cbufferdata.n.a = 65524; + cbufferdata.n.b.w = -2424; + cbufferdata.n.d = 25; + cbufferdata.o = 226; + cbufferdata.p[0].b.z = 2727; + cbufferdata.p[0].d = 28; + cbufferdata.p[1].b.w = 2929; + cbufferdata.q = 30303030303030ULL; + cbufferdata.r = -31313131313131LL; + cbufferdata.s = 19472; // 16.25f + + AllocatedBuffer cb( + allocator, vkh::BufferCreateInfo(sizeof(cbufferdata), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); + + cb.upload(&cbufferdata, sizeof(cbufferdata)); + + VkDescriptorSet descset = allocateDescriptorSet(setlayout); + + vkh::updateDescriptorSets( + device, { + vkh::WriteDescriptorSet(descset, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(cb.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()); + + vkCmdBeginRenderPass( + cmd, vkh::RenderPassBeginInfo(swapRenderPass, swapFramebuffers[swapIndex], scissor, + {vkh::ClearValue(0.0f, 0.0f, 0.0f, 1.0f)}), + VK_SUBPASS_CONTENTS_INLINE); + + 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}); + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe); + 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_Adv_CBuffer_Zoo); \ 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 2617f78c3..be234ae18 100644 --- a/util/test/demos/vk/vk_helpers.h +++ b/util/test/demos/vk/vk_helpers.h @@ -283,6 +283,23 @@ struct PhysicalDeviceProperties2KHR : public VkPhysicalDeviceProperties2KHR operator VkPhysicalDeviceProperties2KHR *() { return this; } }; +struct PhysicalDeviceFeatures2KHR : public VkPhysicalDeviceFeatures2KHR +{ + PhysicalDeviceFeatures2KHR() + { + sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; + pNext = NULL; + } + + PhysicalDeviceFeatures2KHR &next(void *next) + { + this->pNext = next; + return *this; + } + + operator VkPhysicalDeviceFeatures2KHR *() { return this; } +}; + struct SemaphoreCreateInfo : public VkSemaphoreCreateInfo { SemaphoreCreateInfo() : VkSemaphoreCreateInfo() diff --git a/util/test/rdtest/testcase.py b/util/test/rdtest/testcase.py index 48613dcd3..5d29b87c1 100644 --- a/util/test/rdtest/testcase.py +++ b/util/test/rdtest/testcase.py @@ -33,7 +33,7 @@ class ShaderVariableCheck: def type(self, type_: rd.VarType): if self.var.type != type_: raise TestFailureException("Variable {} type mismatch, expected {} but got {}" - .format(self.var.name, type_, self.var.type)) + .format(self.var.name, str(type_), str(self.var.type))) return self @@ -44,9 +44,26 @@ class ShaderVariableCheck: raise TestFailureException("Float variable {} value mismatch, expected {} but got {}" .format(self.var.name, value_, self.var.value.fv[0:count])) else: - if self.var.value.iv[0:count] != value_: - raise TestFailureException("Int variable {} value mismatch, expected {} but got {}" - .format(self.var.name, value_, self.var.value.iv[0:count])) + # hack - check signed and unsigned values + if self.var.value.iv[0:count] != value_ and self.var.value.uv[0:count] != value_: + raise TestFailureException("Int variable {} value mismatch, expected {} but got {} / {}" + .format(self.var.name, value_, self.var.value.iv[0:count], + self.var.value.uv[0:count])) + + return self + + def longvalue(self, value_: list): + count = len(value_) + if isinstance(value_[0], float): + if self.var.value.dv[0:count] != value_: + raise TestFailureException("Float variable {} value mismatch, expected {} but got {}" + .format(self.var.name, value_, self.var.value.dv[0:count])) + else: + # hack - check signed and unsigned values + if self.var.value.s64v[0:count] != value_ and self.var.value.u64v[0:count] != value_: + raise TestFailureException("Int variable {} value mismatch, expected {} but got {} / {}" + .format(self.var.name, value_, self.var.value.s64v[0:count], + self.var.value.u64v[0:count])) return self diff --git a/util/test/tests/Vulkan/VK_Adv_CBuffer_Zoo.py b/util/test/tests/Vulkan/VK_Adv_CBuffer_Zoo.py new file mode 100644 index 000000000..dbc7515c5 --- /dev/null +++ b/util/test/tests/Vulkan/VK_Adv_CBuffer_Zoo.py @@ -0,0 +1,247 @@ +import rdtest +import renderdoc as rd + + +class VK_Adv_CBuffer_Zoo(rdtest.TestCase): + def get_capture(self): + return rdtest.run_and_capture("demos_x64", "VK_Adv_CBuffer_Zoo", 5) + + def check_capture(self): + draw = self.find_draw("Draw") + + self.check(draw is not None) + + self.controller.SetFrameEvent(draw.eventId, False) + + # Make an output so we can pick pixels + out: rd.ReplayOutput = self.controller.CreateOutput(rd.CreateHeadlessWindowingData(), rd.ReplayOutputType.Texture) + + self.check(out is not None) + + out.SetDimensions(100, 100) + + pipe: rd.PipeState = self.controller.GetPipelineState() + + stage = rd.ShaderStage.Vertex + + cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 0, 0) + + var_check = rdtest.ConstantBufferChecker( + self.controller.GetCBufferVariableContents(pipe.GetShader(stage), + pipe.GetShaderEntryPoint(stage), 0, + cbuf.resourceId, cbuf.byteOffset)) + + # For more detailed reference for the below checks, see the commented definition of the cbuffer + # in the shader source code in the demo itself + + # float a; + var_check.check('a').cols(1).rows(1).type(rd.VarType.Float).value([1.0]) + + # vec2 b; + var_check.check('b').cols(2).rows(1).type(rd.VarType.Float).value([2.0, 0.0]) + + # vec3 c; + var_check.check('c').cols(3).rows(1).type(rd.VarType.Float).value([0.0, 3.0]) + + # float d[2]; + var_check.check('d').cols(0).rows(0).arraySize(2).members({ + 0: lambda x: x.cols(1).rows(1).type(rd.VarType.Float).value([4.0]), + 1: lambda x: x.cols(1).rows(1).type(rd.VarType.Float).value([5.0]), + }) + + # mat2x3 e; + var_check.check('e').cols(2).rows(3).column_major().type(rd.VarType.Float).value([6.0, 999.0, + 7.0, 0.0, + 0.0, 0.0]) + + # mat2x3 f[2]; + var_check.check('f').cols(0).rows(0).arraySize(2).members({ + 0: lambda x: x.cols(2).rows(3).column_major().type(rd.VarType.Float).value([8.0, 999.0, + 9.0, 0.0, + 0.0, 0.0]), + 1: lambda x: x.cols(2).rows(3).column_major().type(rd.VarType.Float).value([10.0, 999.0, + 11.0, 0.0, + 0.0, 0.0]), + }) + + # float g; + var_check.check('g').cols(1).rows(1).type(rd.VarType.Float).value([12.0]) + + # struct S + # { + # float a; + # vec2 b; + # double c; + # float d; + # vec3 e; + # float f; + # }; + # S h; + + var_check.check('h').cols(0).rows(0).structSize(6).members({ + 'a': lambda x: x.cols(1).rows(1).type(rd.VarType.Float ).value([0.0]), + 'b': lambda x: x.cols(2).rows(1).type(rd.VarType.Float ).value([0.0]), + 'c': lambda x: x.cols(1).rows(1).type(rd.VarType.Double).longvalue([13.0]), + 'd': lambda x: x.cols(1).rows(1).type(rd.VarType.Float ).value([14.0]), + 'e': lambda x: x.cols(3).rows(1).type(rd.VarType.Float ).value([0.0]), + 'f': lambda x: x.cols(1).rows(1).type(rd.VarType.Float ).value([0.0]), + }) + + # S i[2]; + var_check.check('i').cols(0).rows(0).arraySize(2).members({ + 0: lambda x: x.cols(0).rows(0).structSize(6).members({ + 'a': lambda x: x.cols(1).rows(1).type(rd.VarType.Float ).value([0.0]), + 'b': lambda x: x.cols(2).rows(1).type(rd.VarType.Float ).value([0.0]), + 'c': lambda x: x.cols(1).rows(1).type(rd.VarType.Double).longvalue([15.0]), + 'd': lambda x: x.cols(1).rows(1).type(rd.VarType.Float ).value([0.0]), + 'e': lambda x: x.cols(3).rows(1).type(rd.VarType.Float ).value([0.0]), + 'f': lambda x: x.cols(1).rows(1).type(rd.VarType.Float ).value([0.0]), + }), + 1: lambda x: x.cols(0).rows(0).structSize(6).members({ + 'a': lambda x: x.cols(1).rows(1).type(rd.VarType.Float ).value([0.0]), + 'b': lambda x: x.cols(2).rows(1).type(rd.VarType.Float ).value([0.0]), + 'c': lambda x: x.cols(1).rows(1).type(rd.VarType.Double).longvalue([0.0]), + 'd': lambda x: x.cols(1).rows(1).type(rd.VarType.Float ).value([16.0]), + 'e': lambda x: x.cols(3).rows(1).type(rd.VarType.Float ).value([0.0]), + 'f': lambda x: x.cols(1).rows(1).type(rd.VarType.Float ).value([0.0]), + }), + }) + + # i8vec4 pad1; + var_check.check('pad1') + + # int8_t j; + var_check.check('j').cols(1).rows(1).type(rd.VarType.SByte).value([17]) + + # struct S8 + # { + # int8_t a; + # i8vec4 b; + # i8vec2 c[4]; + # }; + # S8 k; + var_check.check('k').cols(0).rows(0).structSize(3).members({ + 'a': lambda x: x.cols(1).rows(1).type(rd.VarType.SByte).value([0]), + 'b': lambda x: x.cols(4).rows(1).type(rd.VarType.SByte).value([0, 0, 0, 0]), + 'c': lambda x: x.cols(0).rows(0).arraySize(4).members({ + 0: lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value([0, 0]), + 1: lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value([0, 18]), + 2: lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value([0, 0]), + 3: lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value([0, 0]), + }), + }) + + # S8 l[2]; + var_check.check('l').cols(0).rows(0).arraySize(2).members({ + 0: lambda x: x.cols(0).rows(0).structSize(3).members({ + 'a': lambda x: x.cols(1).rows(1).type(rd.VarType.SByte).value([19]), + 'b': lambda x: x.cols(4).rows(1).type(rd.VarType.SByte).value([0, 0, 0, 0]), + 'c': lambda x: x.cols(0).rows(0).arraySize(4).members({ + 0: lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value([0, 0]), + 1: lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value([0, 20]), + 2: lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value([0, 0]), + 3: lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value([0, 0]), + }), + }), + 1: lambda x: x.cols(0).rows(0).structSize(3).members({ + 'a': lambda x: x.cols(1).rows(1).type(rd.VarType.SByte).value([21]), + 'b': lambda x: x.cols(4).rows(1).type(rd.VarType.SByte).value([0, 0, 0, 0]), + 'c': lambda x: x.cols(0).rows(0).arraySize(4).members({ + 0: lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value([0, 22]), + 1: lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value([0, 0]), + 2: lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value([0, 0]), + 3: lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value([0, 0]), + }), + }) + }) + + # int8_t m; + var_check.check('m').cols(1).rows(1).type(rd.VarType.SByte).value([-23]) + + # struct S16 + # { + # uint16_t a; + # i16vec4 b; + # i16vec2 c[4]; + # int8_t d; + # }; + # S16 n; + var_check.check('n').cols(0).rows(0).structSize(4).members({ + 'a': lambda x: x.cols(1).rows(1).type(rd.VarType.UShort).value([65524]), + 'b': lambda x: x.cols(4).rows(1).type(rd.VarType.SShort).value([0, 0, 0, -2424]), + 'c': lambda x: x.cols(0).rows(0).arraySize(4).members({ + 0: lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value([0, 0]), + 1: lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value([0, 0]), + 2: lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value([0, 0]), + 3: lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value([0, 0]), + }), + 'd': lambda x: x.cols(1).rows(1).type(rd.VarType.SByte).value([25]), + }) + + # i8vec4 pad2; + var_check.check('pad2') + + # uint8_t o; + var_check.check('o').cols(1).rows(1).type(rd.VarType.UByte).value([226]) + + # S16 p[2]; + var_check.check('p').cols(0).rows(0).arraySize(2).members({ + 0: lambda x: x.cols(0).rows(0).structSize(4).members({ + 'a': lambda x: x.cols(1).rows(1).type(rd.VarType.UShort).value([0]), + 'b': lambda x: x.cols(4).rows(1).type(rd.VarType.SShort).value([0, 0, 2727, 0]), + 'c': lambda x: x.cols(0).rows(0).arraySize(4).members({ + 0: lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value([0, 0]), + 1: lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value([0, 0]), + 2: lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value([0, 0]), + 3: lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value([0, 0]), + }), + 'd': lambda x: x.cols(1).rows(1).type(rd.VarType.SByte).value([28]), + }), + 1: lambda x: x.cols(0).rows(0).structSize(4).members({ + 'a': lambda x: x.cols(1).rows(1).type(rd.VarType.UShort).value([0]), + 'b': lambda x: x.cols(4).rows(1).type(rd.VarType.SShort).value([0, 0, 0, 2929]), + 'c': lambda x: x.cols(0).rows(0).arraySize(4).members({ + 0: lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value([0, 0]), + 1: lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value([0, 0]), + 2: lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value([0, 0]), + 3: lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value([0, 0]), + }), + 'd': lambda x: x.cols(1).rows(1).type(rd.VarType.SByte).value([0]), + }) + }) + + # i8vec4 pad3; + var_check.check('pad3') + + # uint64_t q; + var_check.check('q').cols(1).rows(1).type(rd.VarType.ULong).longvalue([30303030303030]) + + # int64_t r; + var_check.check('r').cols(1).rows(1).type(rd.VarType.SLong).longvalue([-31313131313131]) + + # half s; + var_check.check('s').cols(1).rows(1).type(rd.VarType.Half).value([16.25]) + + # int8_t test; + var_check.check('test').cols(1).rows(1).type(rd.VarType.SByte).value([42]) + + var_check.done() + + rdtest.log.success("CBuffer variables are as expected") + + tex = rd.TextureDisplay() + tex.resourceId = pipe.GetOutputTargets()[0].resourceId + out.SetTextureDisplay(tex) + + texdetails = self.get_texture(tex.resourceId) + + picked: rd.PixelValue = out.PickPixel(tex.resourceId, False, + int(texdetails.width / 2), int(texdetails.height / 2), 0, 0, 0) + + # We just output green from the shader when the value is as expected + if not rdtest.value_compare(picked.floatValue, [0.0, 1.0, 0.0, 0.0]): + raise rdtest.TestFailureException("Picked value {} doesn't match expectation".format(picked.floatValue)) + + rdtest.log.success("Picked value is as expected") + + out.Shutdown()