diff --git a/util/test/demos/CMakeLists.txt b/util/test/demos/CMakeLists.txt
index 89b47466b..c58e3541a 100644
--- a/util/test/demos/CMakeLists.txt
+++ b/util/test/demos/CMakeLists.txt
@@ -20,6 +20,7 @@ set(VULKAN_SRC
vk/vk_misaligned_dirty.cpp
vk/vk_multi_thread_windows.cpp
vk/vk_overlay_test.cpp
+ vk/vk_parameter_zoo.cpp
vk/vk_resource_lifetimes.cpp
vk/vk_sample_locations.cpp
vk/vk_secondary_cmdbuf.cpp
@@ -48,6 +49,7 @@ set(OPENGL_SRC
gl/gl_mip_gen_rt.cpp
gl/gl_multi_window.cpp
gl/gl_overlay_test.cpp
+ gl/gl_parameter_zoo.cpp
gl/gl_per_type_tex_units.cpp
gl/gl_resource_lifetimes.cpp
gl/gl_runtime_bind_prog_to_pipe.cpp
diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj
index f23674f83..337f6f904 100644
--- a/util/test/demos/demos.vcxproj
+++ b/util/test/demos/demos.vcxproj
@@ -184,6 +184,7 @@
+
@@ -209,6 +210,7 @@
+
diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters
index 79c65916f..bf9291671 100644
--- a/util/test/demos/demos.vcxproj.filters
+++ b/util/test/demos/demos.vcxproj.filters
@@ -336,6 +336,12 @@
Vulkan\demos
+
+ Vulkan\demos
+
+
+ OpenGL\demos
+
diff --git a/util/test/demos/gl/gl_parameter_zoo.cpp b/util/test/demos/gl/gl_parameter_zoo.cpp
new file mode 100644
index 000000000..09e6b8c21
--- /dev/null
+++ b/util/test/demos/gl/gl_parameter_zoo.cpp
@@ -0,0 +1,186 @@
+/******************************************************************************
+ * 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"
+
+#ifdef WIN32
+extern "C" __declspec(dllexport) GLenum APIENTRY InternalFunction()
+{
+ return GL_QUERY_BUFFER;
+}
+#endif
+
+TEST(GL_Parameter_Zoo, OpenGLGraphicsTest)
+{
+ static constexpr const char *Description =
+ "General tests of parameters known to cause problems - e.g. optional values that should be "
+ "ignored, edge cases, special values, etc.";
+
+ const char *vertex = R"EOSHADER(
+#version 450 core
+
+layout(location = 0) in vec3 Position;
+layout(location = 1) in vec4 Color;
+layout(location = 2) in vec2 UV;
+
+uniform int mode;
+
+out vec4 v2fcol;
+
+void main()
+{
+ gl_Position = vec4(Position.xyz * (mode == 1 ? 5.0f : 1.0f), 1);
+ v2fcol = Color;
+}
+
+!!!!)EOSHADER";
+
+ const char *pixel = R"EOSHADER(
+#version 450 core
+
+in vec4 v2fcol;
+
+layout(location = 0, index = 0) out vec4 Color;
+uniform int mode;
+
+void main()
+{
+ if(mode == 1)
+ Color = vec4(0, 0, 1, 0.5);
+ else
+ Color = v2fcol;
+}
+
+)EOSHADER";
+
+ int main()
+ {
+ // initialise, create window, create context, etc
+ if(!Init())
+ 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();
+
+ const char *nullstr = NULL;
+ const char *garbagestr = (const char *)0x1234;
+ GLint len = (int)strlen(vertex) - 5;
+ GLint zero = 0;
+ (void)len;
+
+ GLuint vs = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(vs, 1, &nullstr, &zero);
+ glShaderSource(vs, 1, &garbagestr, &zero);
+ glShaderSource(vs, 1, &vertex, &len);
+ glCompileShader(vs);
+
+ GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(fs, 1, &pixel, NULL);
+ glCompileShader(fs);
+
+ glAttachShader(program, vs);
+ glAttachShader(program, fs);
+ glLinkProgram(program);
+ glDetachShader(program, vs);
+ glDetachShader(program, fs);
+
+ glDeleteShader(vs);
+ glDeleteShader(fs);
+
+ glEnable(GL_SCISSOR_TEST);
+
+ GLuint trash = MakeBuffer();
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, trash);
+ glBufferStorage(GL_PIXEL_UNPACK_BUFFER, 1024, 0, 0);
+ glBindBuffer(GL_PIXEL_PACK_BUFFER, trash);
+
+ if(GLAD_GL_ARB_query_buffer_object)
+ glBindBuffer(GL_QUERY_BUFFER, trash);
+
+#ifdef WIN32
+ PFNGLGETERRORPROC internalFunc =
+ (PFNGLGETERRORPROC)GetProcAddress(GetModuleHandleA(NULL), "InternalFunction");
+
+ if(internalFunc == NULL || internalFunc() != GL_QUERY_BUFFER)
+ {
+ TEST_ERROR("Couldn't query own module for a function");
+ program = 0;
+ }
+#endif
+
+ while(Running())
+ {
+ // trash the texture pack/unpack state
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, screenWidth + screenHeight + 99);
+ glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 5);
+ glPixelStorei(GL_UNPACK_SKIP_PIXELS, 8180);
+ glPixelStorei(GL_UNPACK_SKIP_ROWS, 17);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 8);
+
+ glPixelStorei(GL_PACK_ROW_LENGTH, screenWidth + screenHeight * 1 + 37);
+ glPixelStorei(GL_PACK_SKIP_PIXELS, 9734);
+ glPixelStorei(GL_PACK_SKIP_ROWS, 33);
+ glPixelStorei(GL_PACK_ALIGNMENT, 8);
+
+ glViewport(0, 0, GLsizei(screenWidth), GLsizei(screenHeight));
+ glScissor(0, 0, GLsizei(screenWidth), GLsizei(screenHeight));
+
+ float col[] = {0.4f, 0.5f, 0.6f, 1.0f};
+ glClearBufferfv(GL_COLOR, 0, col);
+
+ glBindVertexArray(vao);
+
+ glUseProgram(program);
+
+ glProgramUniform1i(program, glGetUniformLocation(program, "mode"), 0);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+
+ glScissor(320, 50, 1, 1);
+
+ glProgramUniform1i(program, glGetUniformLocation(program, "mode"), 1);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+
+ Present();
+ }
+
+ return 0;
+ }
+};
+
+REGISTER_TEST();
diff --git a/util/test/demos/vk/vk_parameter_zoo.cpp b/util/test/demos/vk/vk_parameter_zoo.cpp
new file mode 100644
index 000000000..c42f890ed
--- /dev/null
+++ b/util/test/demos/vk/vk_parameter_zoo.cpp
@@ -0,0 +1,452 @@
+/******************************************************************************
+ * 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"
+
+TEST(VK_Parameter_Zoo, VulkanGraphicsTest)
+{
+ static constexpr const char *Description =
+ "General tests of parameters known to cause problems - e.g. optional values that should be "
+ "ignored, edge cases, special values, etc.";
+
+ std::string common = R"EOSHADER(
+
+#version 420 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 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()
+ {
+ optDevExts.push_back(VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME);
+ optDevExts.push_back(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
+
+ // initialise, create window, create context, etc
+ if(!Init())
+ return 3;
+
+ bool KHR_descriptor_update_template =
+ std::find(devExts.begin(), devExts.end(),
+ VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME) != devExts.end();
+ bool KHR_push_descriptor = std::find(devExts.begin(), devExts.end(),
+ VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME) != devExts.end();
+
+ VkDescriptorSetLayout setlayout = createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo({
+ {0, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_VERTEX_BIT},
+ {1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_VERTEX_BIT},
+ {2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_VERTEX_BIT},
+ {3, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_VERTEX_BIT},
+ {4, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT},
+ {5, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT},
+ {6, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT},
+ {7, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT},
+ {8, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT},
+ {9, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT},
+ {10, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, VK_SHADER_STAGE_VERTEX_BIT},
+ }));
+
+ VkDescriptorSetLayout pushlayout = VK_NULL_HANDLE;
+ VkPipelineLayout layout;
+
+ if(KHR_push_descriptor)
+ {
+ pushlayout = createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo(
+ {
+ {5, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_VERTEX_BIT},
+ {10, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT},
+ {20, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT},
+ },
+ VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR));
+
+ layout = createPipelineLayout(vkh::PipelineLayoutCreateInfo({setlayout, pushlayout}));
+ }
+ else
+ {
+ layout = createPipelineLayout(vkh::PipelineLayoutCreateInfo({setlayout}));
+ }
+
+ vkh::GraphicsPipelineCreateInfo pipeCreateInfo;
+
+ pipeCreateInfo.layout = layout;
+ pipeCreateInfo.renderPass = mainWindow->rp;
+
+ 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;
+
+ // invalid handle - should not be used because the flag for derived pipelines is not used
+ pipeCreateInfo.basePipelineHandle = (VkPipeline)0x1234;
+
+ CHECK_VKR(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, pipeCreateInfo, NULL, &pipe));
+ vkDestroyPipeline(device, pipe, NULL);
+
+ pipeCreateInfo.basePipelineHandle = VK_NULL_HANDLE;
+ pipeCreateInfo.basePipelineIndex = 3;
+
+ CHECK_VKR(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, pipeCreateInfo, NULL, &pipe));
+ vkDestroyPipeline(device, pipe, NULL);
+
+ 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);
+
+ VkDescriptorSet descset = allocateDescriptorSet(setlayout);
+
+ VkSampler invalidSampler = (VkSampler)0x1234;
+ VkSampler validSampler = VK_NULL_HANDLE;
+ VkSamplerCreateInfo sampInfo = {VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
+ sampInfo.magFilter = VK_FILTER_LINEAR;
+ sampInfo.minFilter = VK_FILTER_LINEAR;
+ vkCreateSampler(device, &sampInfo, NULL, &validSampler);
+
+ AllocatedBuffer buf(allocator,
+ vkh::BufferCreateInfo(1024, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
+ VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
+ VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT |
+ VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT),
+ VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));
+
+ VkBuffer invalidBuffer = (VkBuffer)0x1234;
+ VkBuffer validBuffer = buf.buffer;
+
+ AllocatedImage img(allocator,
+ vkh::ImageCreateInfo(4, 4, 0, VK_FORMAT_R32G32B32A32_SFLOAT,
+ VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT),
+ VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY}));
+
+ VkImage validImage = img.image;
+
+ VkImageView validImgView = createImageView(
+ vkh::ImageViewCreateInfo(validImage, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R32G32B32A32_SFLOAT));
+ VkImageView invalidImgView = (VkImageView)0x1234;
+
+ {
+ VkCommandBuffer cmd = GetCommandBuffer();
+ vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo());
+ vkh::cmdPipelineBarrier(cmd, {
+ vkh::ImageMemoryBarrier(0, 0, VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_GENERAL, img.image),
+ });
+ vkEndCommandBuffer(cmd);
+ Submit(99, 99, {cmd});
+ }
+
+ VkBufferView validBufView =
+ createBufferView(vkh::BufferViewCreateInfo(validBuffer, VK_FORMAT_R32G32B32A32_SFLOAT));
+ VkBufferView invalidBufView = (VkBufferView)0x1234;
+
+ // initialise the writes with the valid data
+ std::vector validBufInfos = {vkh::DescriptorBufferInfo(validBuffer)};
+ std::vector validBufViews = {validBufView};
+ std::vector validSoloImgs = {
+ vkh::DescriptorImageInfo(validImgView, VK_IMAGE_LAYOUT_GENERAL),
+ };
+ std::vector validCombinedImgs = {
+ vkh::DescriptorImageInfo(validImgView, VK_IMAGE_LAYOUT_GENERAL, validSampler),
+ };
+ std::vector validSamplers = {
+ vkh::DescriptorImageInfo(VK_NULL_HANDLE, VK_IMAGE_LAYOUT_UNDEFINED, validSampler),
+ };
+
+ std::vector writes = {
+ vkh::WriteDescriptorSet(descset, 0, VK_DESCRIPTOR_TYPE_SAMPLER, validSamplers),
+ vkh::WriteDescriptorSet(descset, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ validCombinedImgs),
+ vkh::WriteDescriptorSet(descset, 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, validSoloImgs),
+ vkh::WriteDescriptorSet(descset, 3, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, validSoloImgs),
+
+ vkh::WriteDescriptorSet(descset, 4, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, validBufViews),
+ vkh::WriteDescriptorSet(descset, 5, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, validBufViews),
+
+ vkh::WriteDescriptorSet(descset, 6, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, validBufInfos),
+ vkh::WriteDescriptorSet(descset, 7, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, validBufInfos),
+ vkh::WriteDescriptorSet(descset, 8, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, validBufInfos),
+ vkh::WriteDescriptorSet(descset, 9, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, validBufInfos),
+ };
+
+ // do a first update
+ vkh::updateDescriptorSets(device, writes);
+
+ // set invalid handles but valid pointers and try again
+ VkDescriptorBufferInfo invalidBufInfo = {};
+ invalidBufInfo.buffer = invalidBuffer;
+
+ VkDescriptorImageInfo invalidImgInfo = {};
+ invalidImgInfo.sampler = invalidSampler;
+ invalidImgInfo.imageView = invalidImgView;
+
+ validSoloImgs[0].sampler = invalidSampler;
+ validSamplers[0].imageView = invalidImgView;
+
+ writes[0].pTexelBufferView = &invalidBufView;
+ writes[0].pBufferInfo = &invalidBufInfo;
+
+ writes[1].pTexelBufferView = &invalidBufView;
+ writes[1].pBufferInfo = &invalidBufInfo;
+
+ writes[2].pTexelBufferView = &invalidBufView;
+ writes[2].pBufferInfo = &invalidBufInfo;
+
+ writes[3].pTexelBufferView = &invalidBufView;
+ writes[3].pBufferInfo = &invalidBufInfo;
+
+ writes[4].pImageInfo = &invalidImgInfo;
+ writes[4].pBufferInfo = &invalidBufInfo;
+
+ writes[5].pImageInfo = &invalidImgInfo;
+ writes[5].pBufferInfo = &invalidBufInfo;
+
+ writes[6].pTexelBufferView = &invalidBufView;
+ writes[6].pImageInfo = &invalidImgInfo;
+
+ writes[7].pTexelBufferView = &invalidBufView;
+ writes[7].pImageInfo = &invalidImgInfo;
+
+ writes[8].pTexelBufferView = &invalidBufView;
+ writes[8].pImageInfo = &invalidImgInfo;
+
+ writes[9].pTexelBufferView = &invalidBufView;
+ writes[9].pImageInfo = &invalidImgInfo;
+
+ vkh::updateDescriptorSets(device, writes);
+
+ // finally set invalid pointers too
+ VkBufferView *invalidBufViews = (VkBufferView *)0x1234;
+ vkh::DescriptorBufferInfo *invalidBufInfos = (vkh::DescriptorBufferInfo *)0x1234;
+ vkh::DescriptorImageInfo *invalidImgInfos = (vkh::DescriptorImageInfo *)0x1234;
+
+ writes[0].pTexelBufferView = invalidBufViews;
+ writes[0].pBufferInfo = invalidBufInfos;
+
+ writes[1].pTexelBufferView = invalidBufViews;
+ writes[1].pBufferInfo = invalidBufInfos;
+
+ writes[2].pTexelBufferView = invalidBufViews;
+ writes[2].pBufferInfo = invalidBufInfos;
+
+ writes[3].pTexelBufferView = invalidBufViews;
+ writes[3].pBufferInfo = invalidBufInfos;
+
+ writes[4].pImageInfo = invalidImgInfos;
+ writes[4].pBufferInfo = invalidBufInfos;
+
+ writes[5].pImageInfo = invalidImgInfos;
+ writes[5].pBufferInfo = invalidBufInfos;
+
+ writes[6].pTexelBufferView = invalidBufViews;
+ writes[6].pImageInfo = invalidImgInfos;
+
+ writes[7].pTexelBufferView = invalidBufViews;
+ writes[7].pImageInfo = invalidImgInfos;
+
+ writes[8].pTexelBufferView = invalidBufViews;
+ writes[8].pImageInfo = invalidImgInfos;
+
+ writes[9].pTexelBufferView = invalidBufViews;
+ writes[9].pImageInfo = invalidImgInfos;
+
+ vkh::updateDescriptorSets(device, writes);
+
+ if(KHR_descriptor_update_template)
+ {
+ struct datastruct
+ {
+ VkBufferView view;
+ VkDescriptorBufferInfo buf;
+ VkDescriptorImageInfo img;
+ VkDescriptorImageInfo combined;
+ VkDescriptorImageInfo sampler;
+ } data;
+
+ data.view = validBufView;
+ data.buf = validBufInfos[0];
+ data.img = vkh::DescriptorImageInfo(validImgView, VK_IMAGE_LAYOUT_GENERAL);
+ data.combined = vkh::DescriptorImageInfo(validImgView, VK_IMAGE_LAYOUT_GENERAL, validSampler);
+ data.sampler =
+ vkh::DescriptorImageInfo(VK_NULL_HANDLE, VK_IMAGE_LAYOUT_UNDEFINED, validSampler);
+ data.img.sampler = invalidSampler;
+ data.sampler.imageView = invalidImgView;
+
+ std::vector entries = {
+ // descriptor count 0 updates are allowed
+ {0, 0, 0, VK_DESCRIPTOR_TYPE_SAMPLER, 0, sizeof(data)},
+ {0, 0, 1, VK_DESCRIPTOR_TYPE_SAMPLER, offsetof(datastruct, sampler), sizeof(data)},
+ {1, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, offsetof(datastruct, combined),
+ sizeof(data)},
+ {2, 0, 1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, offsetof(datastruct, img), sizeof(data)},
+ {3, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, offsetof(datastruct, img), sizeof(data)},
+ {4, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, offsetof(datastruct, view), sizeof(data)},
+ {5, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, offsetof(datastruct, view), sizeof(data)},
+ {6, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, offsetof(datastruct, buf), sizeof(data)},
+ {7, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, offsetof(datastruct, buf), sizeof(data)},
+ {8, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, offsetof(datastruct, buf),
+ sizeof(data)},
+ {9, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, offsetof(datastruct, buf),
+ sizeof(data)},
+ };
+
+ VkDescriptorUpdateTemplateCreateInfoKHR createInfo = {};
+ createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR;
+ createInfo.descriptorUpdateEntryCount = (uint32_t)entries.size();
+ createInfo.pDescriptorUpdateEntries = entries.data();
+ createInfo.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR;
+ createInfo.descriptorSetLayout = setlayout;
+ createInfo.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ createInfo.pipelineLayout = layout;
+ createInfo.set = 0;
+ VkDescriptorUpdateTemplateKHR templ;
+ vkCreateDescriptorUpdateTemplateKHR(device, &createInfo, NULL, &templ);
+
+ vkUpdateDescriptorSetWithTemplateKHR(device, descset, templ, &data);
+
+ vkDestroyDescriptorUpdateTemplateKHR(device, templ, NULL);
+ }
+
+ struct pushdatastruct
+ {
+ VkDescriptorBufferInfo buf;
+ } pushdata;
+
+ pushdata.buf = validBufInfos[0];
+
+ VkDescriptorUpdateTemplateKHR pushtempl = VK_NULL_HANDLE;
+ if(KHR_descriptor_update_template && KHR_push_descriptor)
+ {
+ std::vector entries = {
+ {0, 0, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, sizeof(pushdata)},
+ {10, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, sizeof(pushdata)},
+ };
+
+ VkDescriptorUpdateTemplateCreateInfoKHR createInfo = {};
+ createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR;
+ createInfo.descriptorUpdateEntryCount = (uint32_t)entries.size();
+ createInfo.pDescriptorUpdateEntries = entries.data();
+ createInfo.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR;
+ createInfo.descriptorSetLayout = pushlayout;
+ createInfo.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ createInfo.pipelineLayout = layout;
+ createInfo.set = 1;
+ vkCreateDescriptorUpdateTemplateKHR(device, &createInfo, NULL, &pushtempl);
+ }
+
+ 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(mainWindow->rp, mainWindow->GetFB(), mainWindow->scissor),
+ VK_SUBPASS_CONTENTS_INLINE);
+
+ vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
+ vkCmdSetViewport(cmd, 0, 1, &mainWindow->viewport);
+ vkCmdSetScissor(cmd, 0, 1, &mainWindow->scissor);
+ vkh::cmdBindVertexBuffers(cmd, 0, {vb.buffer}, {0});
+ vkh::cmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, {descset}, {0, 0});
+ vkCmdPushDescriptorSetKHR(
+ cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 1, 1,
+ vkh::WriteDescriptorSet(VK_NULL_HANDLE, 20, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ validBufInfos));
+ if(KHR_descriptor_update_template && KHR_push_descriptor)
+ vkCmdPushDescriptorSetWithTemplateKHR(cmd, pushtempl, layout, 1, &pushdata);
+ 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();
+ }
+
+ if(KHR_descriptor_update_template && KHR_push_descriptor)
+ vkDestroyDescriptorUpdateTemplateKHR(device, pushtempl, NULL);
+
+ return 0;
+ }
+};
+
+REGISTER_TEST();
diff --git a/util/test/rdtest/util.py b/util/test/rdtest/util.py
index e467bd25b..5be9c535d 100644
--- a/util/test/rdtest/util.py
+++ b/util/test/rdtest/util.py
@@ -141,6 +141,15 @@ def sanitise_filename(name: str):
return re.sub('^/', '', name)
+def linear_to_SRGB(val):
+ if type(val) == float:
+ if val <= 0.0031308:
+ return val * 12.92
+ else:
+ return 1.055 * math.pow(val, 1.0 / 2.4) - 0.055
+
+ return [linear_to_SRGB(v) for v in val]
+
def png_save(out_path: str, rows: List[bytes], dimensions: Tuple[int, int], has_alpha: bool):
try:
f = open(out_path, 'wb')
diff --git a/util/test/tests/GL/GL_Parameter_Zoo.py b/util/test/tests/GL/GL_Parameter_Zoo.py
new file mode 100644
index 000000000..ebc264aea
--- /dev/null
+++ b/util/test/tests/GL/GL_Parameter_Zoo.py
@@ -0,0 +1,106 @@
+import struct
+import math
+import renderdoc as rd
+import rdtest
+
+
+class GL_Parameter_Zoo(rdtest.TestCase):
+ demos_test_name = 'GL_Parameter_Zoo'
+
+ def check_capture(self):
+ id = self.get_last_draw().copyDestination
+
+ tex_details = self.get_texture(id)
+
+ self.controller.SetFrameEvent(self.get_last_draw().eventId, True)
+
+ data = self.controller.GetTextureData(id, 0, 0)
+ first_pixel = struct.unpack_from("BBBB", data, 0)
+
+ val = [int(round(v * 255)) for v in rdtest.linear_to_SRGB([0.4, 0.5, 0.6, 1.0])]
+ if not rdtest.value_compare(first_pixel, val):
+ raise rdtest.TestFailureException("First pixel should be clear color {}, not {}".format(val, first_pixel))
+
+ magic_pixel = struct.unpack_from("BBBB", data, (50 * tex_details.width + 320) * 4)
+
+ if not rdtest.value_compare(magic_pixel, [0, 0, 255, 127]):
+ raise rdtest.TestFailureException("Pixel @ 320,50 should be blue: {}, not {}".format(val, magic_pixel))
+
+ rdtest.log.success("Decoded pixels from texture data are correct")
+
+ img_path = rdtest.get_tmp_path('preserved_alpha.png')
+
+ self.controller.SetFrameEvent(self.get_last_draw().eventId, True)
+
+ save_data = rd.TextureSave()
+ save_data.resourceId = id
+ save_data.destType = rd.FileType.PNG
+ save_data.alpha = rd.AlphaMapping.Discard # this should not discard the alpha
+
+ self.controller.SaveTexture(save_data, img_path)
+
+ data = rdtest.png_load_data(img_path)
+
+ magic_pixel = struct.unpack_from("BBBB", data[-1-50], 320 * 4)
+
+ if not rdtest.value_compare(magic_pixel, [0, 0, 255, 127]):
+ raise rdtest.TestFailureException("Pixel @ 320,50 should be blue: {}, not {}".format(val, magic_pixel))
+
+ draw = self.find_draw("Draw")
+
+ self.controller.SetFrameEvent(draw.eventId, False)
+
+ postvs_data = self.get_postvs(rd.MeshDataStage.VSOut, 0, draw.numIndices)
+
+ postvs_ref = {
+ 0: {
+ 'vtx': 0,
+ 'idx': 0,
+ 'gl_Position': [-0.5, -0.5, 0.0, 1.0],
+ 'v2fcol': [1.0, 0.0, 0.0, 1.0],
+ },
+ 1: {
+ 'vtx': 1,
+ 'idx': 1,
+ 'gl_Position': [0.0, 0.5, 0.0, 1.0],
+ 'v2fcol': [0.0, 1.0, 0.0, 1.0],
+ },
+ 2: {
+ 'vtx': 2,
+ 'idx': 2,
+ 'gl_Position': [0.5, -0.5, 0.0, 1.0],
+ 'v2fcol': [0.0, 0.0, 1.0, 1.0],
+ },
+ }
+
+ self.check_mesh_data(postvs_ref, postvs_data)
+
+ results = self.controller.FetchCounters([rd.GPUCounter.RasterizedPrimitives, rd.GPUCounter.VSInvocations, rd.GPUCounter.FSInvocations])
+
+ results = [r for r in results if r.eventId == draw.eventId]
+
+ if len(results) != 3:
+ raise rdtest.TestFailureException("Expected 3 results, got {} results".format(len(results)))
+
+ for r in results:
+ r: rd.CounterResult
+ val = r.value.u32
+ if r.counter == rd.GPUCounter.RasterizedPrimitives:
+ if not rdtest.value_compare(val, 1):
+ raise rdtest.TestFailureException("RasterizedPrimitives result {} is not as expected".format(val))
+ else:
+ rdtest.log.success("RasterizedPrimitives result is as expected")
+ elif r.counter == rd.GPUCounter.VSInvocations:
+ if not rdtest.value_compare(val, 3):
+ raise rdtest.TestFailureException("VSInvocations result {} is not as expected".format(val))
+ else:
+ rdtest.log.success("VSInvocations result is as expected")
+ elif r.counter == rd.GPUCounter.FSInvocations:
+ if val < int(0.1 * tex_details.width * tex_details.height):
+ raise rdtest.TestFailureException("FSInvocations result {} is not as expected".format(val))
+ else:
+ rdtest.log.success("FSInvocations result is as expected")
+ else:
+ raise rdtest.TestFailureException("Unexpected counter result {}".format(r.counter))
+
+ rdtest.log.success("Counter data retrieved successfully")
diff --git a/util/test/tests/Vulkan/VK_Parameter_Zoo.py b/util/test/tests/Vulkan/VK_Parameter_Zoo.py
new file mode 100644
index 000000000..7d9faffd1
--- /dev/null
+++ b/util/test/tests/Vulkan/VK_Parameter_Zoo.py
@@ -0,0 +1,42 @@
+import renderdoc as rd
+import rdtest
+
+
+class VK_Parameter_Zoo(rdtest.TestCase):
+ demos_test_name = 'VK_Parameter_Zoo'
+
+ def check_capture(self):
+ draw = self.find_draw("Draw")
+
+ self.controller.SetFrameEvent(draw.eventId, False)
+
+ postvs_data = self.get_postvs(rd.MeshDataStage.VSOut, 0, draw.numIndices)
+
+ postvs_ref = {
+ 0: {
+ 'vtx': 0,
+ 'idx': 0,
+ 'gl_PerVertex.gl_Position': [-0.5, 0.5, 0.0, 1.0],
+ 'vertOut.pos': [-0.5, 0.5, 0.0, 1.0],
+ 'vertOut.col': [1.0, 0.0, 0.0, 1.0],
+ 'vertOut.uv': [0.0, 0.0, 0.0, 1.0],
+ },
+ 1: {
+ 'vtx': 1,
+ 'idx': 1,
+ 'gl_PerVertex.gl_Position': [0.0, -0.5, 0.0, 1.0],
+ 'vertOut.pos': [0.0, -0.5, 0.0, 1.0],
+ 'vertOut.col': [0.0, 1.0, 0.0, 1.0],
+ 'vertOut.uv': [0.0, 1.0, 0.0, 1.0],
+ },
+ 2: {
+ 'vtx': 2,
+ 'idx': 2,
+ 'gl_PerVertex.gl_Position': [0.5, 0.5, 0.0, 1.0],
+ 'vertOut.pos': [0.5, 0.5, 0.0, 1.0],
+ 'vertOut.col': [0.0, 0.0, 1.0, 1.0],
+ 'vertOut.uv': [1.0, 0.0, 0.0, 1.0],
+ },
+ }
+
+ self.check_mesh_data(postvs_ref, postvs_data)