Uses the extension to write to two different layers of an image, then merge those side-by-side in a final image. ---
This patch is available in my 'multiview' branch: https://gitlab.freedesktop.org/cmarcelo/crucible/commits/multiview Makefile.am | 2 + data/func.multiview.ref.png | Bin 0 -> 1878 bytes src/tests/func/multiview.c | 373 ++++++++++++++++++++++++++++++++++++ 3 files changed, 375 insertions(+) create mode 100644 data/func.multiview.ref.png create mode 100644 src/tests/func/multiview.c diff --git a/Makefile.am b/Makefile.am index dc24a99..621ee83 100644 --- a/Makefile.am +++ b/Makefile.am @@ -99,6 +99,7 @@ bin_crucible_SOURCES = \ src/tests/func/compute-num-workgroups.c \ src/tests/func/interleaved-cmd-buffers.c \ src/tests/func/miptree/miptree.c \ + src/tests/func/multiview.c \ src/tests/func/push-constants/basic.c \ src/tests/func/shader/constants.c \ src/tests/func/shader/fragcoord.c \ @@ -145,6 +146,7 @@ BUILT_SOURCES = \ src/tests/func/compute-num-workgroups-spirv.h \ src/tests/func/miptree/miptree-spirv.h \ src/tests/func/miptree/miptree_gen.c \ + src/tests/func/multiview-spirv.h \ src/tests/func/push-constants/basic-spirv.h \ src/tests/func/amd/gcn_shader-spirv.h \ src/tests/func/amd/shader_trinary_minmax-spirv.h \ diff --git a/data/func.multiview.ref.png b/data/func.multiview.ref.png new file mode 100644 index 0000000000000000000000000000000000000000..dfc81c2b6b3c60886dee1c006756e8b596ff8391 GIT binary patch literal 1878 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 literal 0 HcmV?d00001 diff --git a/src/tests/func/multiview.c b/src/tests/func/multiview.c new file mode 100644 index 0000000..bd0767f --- /dev/null +++ b/src/tests/func/multiview.c @@ -0,0 +1,373 @@ +// Copyright 2018 Intel Corporation +// +// 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 (including the next +// paragraph) 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 "tapi/t.h" + +#include "multiview-spirv.h" + +// Uses two subpasses, the first pass writes using VK_KHR_multiview to +// a two layer image, with a displacement based on the gl_ViewIndex. +// The second pass takes both layers as input and render them side by +// side. + +static void +test_multiview(void) +{ + t_require_ext("VK_KHR_multiview"); + + VkRenderPassMultiviewCreateInfo multiviewInfo = { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO, + .subpassCount = 2, + .pViewMasks = (uint32_t[]) { 3, 1 }, + }; + + VkRenderPass pass = qoCreateRenderPass(t_device, + .pNext = &multiviewInfo, + .attachmentCount = 4, + .pAttachments = (VkAttachmentDescription[]) { + { + // Multi layered image written in first subpass. + QO_ATTACHMENT_DESCRIPTION_DEFAULTS, + .format = VK_FORMAT_R8G8B8A8_UNORM, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }, + { + QO_ATTACHMENT_DESCRIPTION_DEFAULTS, + .format = VK_FORMAT_R8G8B8A8_UNORM, + .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }, + { + QO_ATTACHMENT_DESCRIPTION_DEFAULTS, + .format = VK_FORMAT_R8G8B8A8_UNORM, + .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }, + { + // Final output merging both layers. + QO_ATTACHMENT_DESCRIPTION_DEFAULTS, + .format = VK_FORMAT_R8G8B8A8_UNORM, + .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + }, + }, + .subpassCount = 2, + .pSubpasses = (VkSubpassDescription[]) { + { + QO_SUBPASS_DESCRIPTION_DEFAULTS, + .colorAttachmentCount = 1, + .pColorAttachments = (VkAttachmentReference[]) { + { + .attachment = 0, + .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + }, + }, + }, + { + QO_SUBPASS_DESCRIPTION_DEFAULTS, + .inputAttachmentCount = 2, + .pInputAttachments = (VkAttachmentReference[]) { + { + .attachment = 1, + .layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }, + { + .attachment = 2, + .layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + } + }, + .colorAttachmentCount = 1, + .pColorAttachments = (VkAttachmentReference[]) { + { + .attachment = 3, + .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + }, + }, + }, + }, + .dependencyCount = 2, + .pDependencies = (VkSubpassDependency[]) { + { + .srcSubpass = VK_SUBPASS_EXTERNAL, + .dstSubpass = 0, + .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask = 0, + .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + }, + { + .srcSubpass = 0, + .dstSubpass = 1, + .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, + .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, + }, + }); + + VkShaderModule first_vs = qoCreateShaderModuleGLSL(t_device, VERTEX, + QO_EXTENSION GL_EXT_multiview : enable + + layout(location = 0) out vec4 v_color; + + vec4 positions[3] = vec4[]( + vec4(-0.5, -0.5, 0, 1), + vec4(-0.2, 0.5, 0, 1), + vec4(-0.8, 0.5, 0, 1) + ); + + vec4 colors[3] = vec4[]( + vec4(1, 1, 0, 1), + vec4(1, 0, 1, 1), + vec4(0, 1, 1, 1) + ); + + vec4 displacement[2] = vec4[]( + vec4(0, 0, 0, 0), + vec4(1, 0.2, 0, 0) + ); + + void main() + { + gl_Position = displacement[gl_ViewIndex] + positions[gl_VertexIndex]; + v_color = colors[gl_VertexIndex]; + } + ); + + VkShaderModule first_fs = qoCreateShaderModuleGLSL(t_device, FRAGMENT, + layout(location = 0) in vec4 v_color; + layout(location = 0) out vec4 f_color; + + void main() + { + f_color = v_color; + } + ); + + + VkPipelineLayout first_layout = qoCreatePipelineLayout(t_device); + + VkPipeline first_pipeline = qoCreateGraphicsPipeline(t_device, t_pipeline_cache, + &(QoExtraGraphicsPipelineCreateInfo) { + QO_EXTRA_GRAPHICS_PIPELINE_CREATE_INFO_DEFAULTS, + .vertexShader = first_vs, + .fragmentShader = first_fs, + .pNext = + &(VkGraphicsPipelineCreateInfo) { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pInputAssemblyState = &(VkPipelineInputAssemblyStateCreateInfo) { + QO_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO_DEFAULTS, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, + }, + .pColorBlendState = &(VkPipelineColorBlendStateCreateInfo) { + QO_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO_DEFAULTS + }, + .pVertexInputState = &(VkPipelineVertexInputStateCreateInfo) {}, + .renderPass = pass, + .layout = first_layout, + .subpass = 0, + }}); + + VkShaderModule second_vs = qoCreateShaderModuleGLSL(t_device, VERTEX, + vec2 positions[6] = vec2[]( + vec2( -1, -1 ), + vec2( 1, -1 ), + vec2( 1, 1 ), + vec2( 1, 1 ), + vec2( -1, 1 ), + vec2( -1, -1 ) + ); + + void main() + { + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + } + ); + + VkShaderModule second_fs = qoCreateShaderModuleGLSL(t_device, FRAGMENT, + layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput theInput[2]; + layout(location = 0) out vec4 f_color; + + void main() + { + f_color.a = 1.0; + f_color.r = subpassLoad(theInput[0]).r; + f_color.g = subpassLoad(theInput[1]).g; + f_color.b = 0.0; + } + ); + + VkDescriptorSetLayout set_layout = qoCreateDescriptorSetLayout(t_device, + .bindingCount = 1, + .pBindings = (VkDescriptorSetLayoutBinding[]) { + { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, + .descriptorCount = 2, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + }, + }); + + VkPipelineLayout second_layout = qoCreatePipelineLayout(t_device, + .setLayoutCount = 1, + .pSetLayouts = &set_layout); + + VkPipeline second_pipeline = qoCreateGraphicsPipeline(t_device, t_pipeline_cache, + &(QoExtraGraphicsPipelineCreateInfo) { + QO_EXTRA_GRAPHICS_PIPELINE_CREATE_INFO_DEFAULTS, + .vertexShader = second_vs, + .fragmentShader = second_fs, + .pNext = + &(VkGraphicsPipelineCreateInfo) { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pInputAssemblyState = &(VkPipelineInputAssemblyStateCreateInfo) { + QO_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO_DEFAULTS, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, + }, + .pColorBlendState = &(VkPipelineColorBlendStateCreateInfo) { + QO_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO_DEFAULTS + }, + .pVertexInputState = &(VkPipelineVertexInputStateCreateInfo) {}, + .renderPass = pass, + .layout = second_layout, + .subpass = 1, + }}); + + VkImage layered_image = qoCreateImage(t_device, + .imageType = VK_IMAGE_TYPE_2D, + .format = VK_FORMAT_R8G8B8A8_UNORM, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT, + .mipLevels = 1, + .arrayLayers = 2, + .extent = { + .width = t_width, + .height = t_height, + .depth = 1, + }); + + VkMemoryRequirements mem_reqs; + vkGetImageMemoryRequirements(t_device, layered_image, &mem_reqs); + + VkDeviceMemory layered_mem = qoAllocMemoryFromRequirements(t_device, &mem_reqs); + qoBindImageMemory(t_device, layered_image, layered_mem, 0); + + VkImageView full_image_view = qoCreateImageView(t_device, + QO_IMAGE_VIEW_CREATE_INFO_DEFAULTS, + .format = VK_FORMAT_R8G8B8A8_UNORM, + .image = layered_image, + .viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY, + .subresourceRange.layerCount = 2); + + VkImageView first_image_view = qoCreateImageView(t_device, + QO_IMAGE_VIEW_CREATE_INFO_DEFAULTS, + .format = VK_FORMAT_R8G8B8A8_UNORM, + .image = layered_image, + .viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY, + .subresourceRange.layerCount = 1); + + VkImageView second_image_view = qoCreateImageView(t_device, + QO_IMAGE_VIEW_CREATE_INFO_DEFAULTS, + .format = VK_FORMAT_R8G8B8A8_UNORM, + .image = layered_image, + .viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY, + .subresourceRange.baseArrayLayer = 1, + .subresourceRange.layerCount = 1); + + VkImageView attachments[] = { + full_image_view, + first_image_view, + second_image_view, + + // We create a new framebuffer but reuse the framework's image + // view as a final output, so we get the bootstrapping and + // comparing logic for free. + t_color_image_view, + }; + + VkFramebuffer framebuffer = qoCreateFramebuffer(t_device, + .renderPass = pass, + .width = t_width, + .height = t_height, + .layers = 1, + .attachmentCount = 4, + .pAttachments = attachments); + + VkDescriptorSet set = qoAllocateDescriptorSet(t_device, + .descriptorPool = t_descriptor_pool, + .pSetLayouts = &set_layout); + + vkUpdateDescriptorSets(t_device, + 1, /* writeCount */ + &(VkWriteDescriptorSet) { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = set, + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, + .descriptorCount = 2, + .pImageInfo = (VkDescriptorImageInfo[]) { + { + .imageView = first_image_view, + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }, + { + .imageView = second_image_view, + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }, + }, + }, 0, NULL); + + vkCmdBeginRenderPass(t_cmd_buffer, + &(VkRenderPassBeginInfo) { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .renderPass = pass, + .framebuffer = framebuffer, + .renderArea = { { 0, 0 }, { t_width, t_height } }, + .clearValueCount = 4, + .pClearValues = (VkClearValue[]) { + { .color = { .float32 = {0.0, 0.0, 0.0, 1.0} } }, + { .color = { .float32 = {0.0, 0.0, 0.0, 1.0} } }, + { .color = { .float32 = {0.0, 0.0, 0.0, 1.0} } }, + { .color = { .float32 = {0.0, 0.0, 0.0, 1.0} } }, + } + }, VK_SUBPASS_CONTENTS_INLINE); + + // Draw triangle in both views, with displacement based on the view index. + vkCmdBindPipeline(t_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, first_pipeline); + vkCmdDraw(t_cmd_buffer, 3, 1, 0, 0); + + // Render the views side by side. + vkCmdNextSubpass(t_cmd_buffer, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(t_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, second_pipeline); + vkCmdBindDescriptorSets(t_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, second_layout, + 0, 1, &set, 0, NULL); + vkCmdDraw(t_cmd_buffer, 6, 1, 0, 0); + + vkCmdEndRenderPass(t_cmd_buffer); + qoEndCommandBuffer(t_cmd_buffer); + qoQueueSubmit(t_queue, 1, &t_cmd_buffer, VK_NULL_HANDLE); +} + +test_define { + .name = "func.multiview", + .start = test_multiview, +}; -- 2.19.0 _______________________________________________ Piglit mailing list Piglit@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/piglit