diff --git a/include/render/vulkan.h b/include/render/vulkan.h index abeb11cc5..3aa909f17 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -271,7 +271,8 @@ struct wlr_vk_renderer { VkShaderModule vert_module; VkShaderModule tex_frag_module; VkShaderModule quad_frag_module; - VkShaderModule output_module; + VkShaderModule output_module_srgb; + VkShaderModule output_module_3d_lut; struct wl_list pipeline_layouts; // struct wlr_vk_pipeline_layout.link @@ -280,19 +281,10 @@ struct wlr_vk_renderer { VkDescriptorSetLayout output_ds_srgb_layout; VkDescriptorSetLayout output_ds_lut3d_layout; VkSampler output_sampler_lut3d; - // descriptor set indicating dummy 1x1x1 image, for use in the lut3d slot - VkDescriptorSet output_ds_lut3d_dummy; - struct wlr_vk_descriptor_pool *output_ds_lut3d_dummy_pool; size_t last_output_pool_size; struct wl_list output_descriptor_pools; // wlr_vk_descriptor_pool.link - // dummy sampler to bind when output shader is not using a lookup table - VkImage dummy3d_image; - VkDeviceMemory dummy3d_mem; - VkImageView dummy3d_image_view; - bool dummy3d_image_transitioned; - VkSemaphore timeline_semaphore; uint64_t timeline_point; diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 032fcc7b2..e790faa87 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -179,44 +179,41 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { .uv_size = { 1, 1 }, }; - size_t dim = 1; - if (pass->color_transform && pass->color_transform->type == COLOR_TRANSFORM_LUT_3D) { - struct wlr_color_transform_lut3d *lut3d = - wlr_color_transform_lut3d_from_base(pass->color_transform); - dim = lut3d->dim_len; - } - - struct wlr_vk_frag_output_pcr_data frag_pcr_data = { - .lut_3d_offset = 0.5f / dim, - .lut_3d_scale = (float)(dim - 1) / dim, - }; mat3_to_mat4(final_matrix, vert_pcr_data.mat4); - - if (pass->color_transform) { - bind_pipeline(pass, render_buffer->plain.render_setup->output_pipe_lut3d); - } else { - bind_pipeline(pass, render_buffer->plain.render_setup->output_pipe_srgb); - } vkCmdPushConstants(render_cb->vk, renderer->output_pipe_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vert_pcr_data), &vert_pcr_data); - vkCmdPushConstants(render_cb->vk, renderer->output_pipe_layout, - VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(vert_pcr_data), - sizeof(frag_pcr_data), &frag_pcr_data); - VkDescriptorSet lut_ds; - if (pass->color_transform && pass->color_transform->type == COLOR_TRANSFORM_LUT_3D) { - struct wlr_vk_color_transform *transform = - get_color_transform(pass->color_transform, renderer); - assert(transform); - lut_ds = transform->lut_3d.ds; - } else { - lut_ds = renderer->output_ds_lut3d_dummy; + VkDescriptorSet ds[2]; + size_t ds_len = 0; + ds[ds_len++] = render_buffer->plain.blend_descriptor_set; + + VkPipeline pl = render_buffer->plain.render_setup->output_pipe_srgb; + if (pass->color_transform) { + switch (pass->color_transform->type){ + case COLOR_TRANSFORM_LUT_3D: { + pl = render_buffer->plain.render_setup->output_pipe_lut3d; + struct wlr_vk_color_transform *transform = + get_color_transform(pass->color_transform, renderer); + ds[ds_len++] = transform->lut_3d.ds; + + struct wlr_color_transform_lut3d *lut = + wlr_color_transform_lut3d_from_base(pass->color_transform); + struct wlr_vk_frag_output_pcr_data frag_pcr_data = { + .lut_3d_offset = 0.5f / lut->dim_len, + .lut_3d_scale = (float)(lut->dim_len - 1) / lut->dim_len, + }; + vkCmdPushConstants(render_cb->vk, renderer->output_pipe_layout, + VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(vert_pcr_data), + sizeof(frag_pcr_data), &frag_pcr_data); + + break; + } + case COLOR_TRANSFORM_SRGB: + break; + } } - VkDescriptorSet ds[] = { - render_buffer->plain.blend_descriptor_set, // set 0 - lut_ds, // set 1 - }; - size_t ds_len = sizeof(ds) / sizeof(ds[0]); + + bind_pipeline(pass, pl); vkCmdBindDescriptorSets(render_cb->vk, VK_PIPELINE_BIND_POINT_GRAPHICS, renderer->output_pipe_layout, 0, ds_len, ds, 0, NULL); @@ -1100,14 +1097,6 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend return NULL; } - if (!renderer->dummy3d_image_transitioned) { - renderer->dummy3d_image_transitioned = true; - vulkan_change_layout(cb->vk, renderer->dummy3d_image, - VK_IMAGE_LAYOUT_UNDEFINED, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - 0, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_ACCESS_SHADER_READ_BIT); - } - int width = buffer->wlr_buffer->width; int height = buffer->wlr_buffer->height; VkRect2D rect = { .extent = { width, height } }; diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 4051dbf05..ddd5f7682 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -25,7 +25,8 @@ #include "render/vulkan/shaders/common.vert.h" #include "render/vulkan/shaders/texture.frag.h" #include "render/vulkan/shaders/quad.frag.h" -#include "render/vulkan/shaders/output.frag.h" +#include "render/vulkan/shaders/output_inverse_rgb.frag.h" +#include "render/vulkan/shaders/output_lut_3d.frag.h" #include "types/wlr_buffer.h" #include "util/time.h" @@ -1120,7 +1121,8 @@ static void vulkan_destroy(struct wlr_renderer *wlr_renderer) { vkDestroyShaderModule(dev->dev, renderer->vert_module, NULL); vkDestroyShaderModule(dev->dev, renderer->tex_frag_module, NULL); vkDestroyShaderModule(dev->dev, renderer->quad_frag_module, NULL); - vkDestroyShaderModule(dev->dev, renderer->output_module, NULL); + vkDestroyShaderModule(dev->dev, renderer->output_module_srgb, NULL); + vkDestroyShaderModule(dev->dev, renderer->output_module_3d_lut, NULL); struct wlr_vk_pipeline_layout *pipeline_layout, *pipeline_layout_tmp; wl_list_for_each_safe(pipeline_layout, pipeline_layout_tmp, @@ -1132,10 +1134,6 @@ static void vulkan_destroy(struct wlr_renderer *wlr_renderer) { free(pipeline_layout); } - vkDestroyImageView(dev->dev, renderer->dummy3d_image_view, NULL); - vkDestroyImage(dev->dev, renderer->dummy3d_image, NULL); - vkFreeMemory(dev->dev, renderer->dummy3d_mem, NULL); - vkDestroySemaphore(dev->dev, renderer->timeline_semaphore, NULL); vkDestroyPipelineLayout(dev->dev, renderer->output_pipe_layout, NULL); vkDestroyDescriptorSetLayout(dev->dev, renderer->output_ds_srgb_layout, NULL); @@ -1800,18 +1798,15 @@ static bool init_blend_to_output_pipeline(struct wlr_vk_renderer *renderer, VkResult res; VkDevice dev = renderer->dev->dev; - uint32_t output_transform_type = transform; - VkSpecializationMapEntry spec_entry = { - .constantID = 0, - .offset = 0, - .size = sizeof(uint32_t), - }; - VkSpecializationInfo specialization = { - .mapEntryCount = 1, - .pMapEntries = &spec_entry, - .dataSize = sizeof(uint32_t), - .pData = &output_transform_type, - }; + VkShaderModule output_module; + switch(transform) { + case WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB: + output_module = renderer->output_module_srgb; + break; + case WLR_VK_OUTPUT_TRANSFORM_LUT3D: + output_module = renderer->output_module_3d_lut; + break; + } VkPipelineShaderStageCreateInfo tex_stages[2] = { { @@ -1823,9 +1818,8 @@ static bool init_blend_to_output_pipeline(struct wlr_vk_renderer *renderer, { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, - .module = renderer->output_module, + .module = output_module, .pName = "main", - .pSpecializationInfo = &specialization, }, }; @@ -1992,105 +1986,6 @@ struct wlr_vk_pipeline_layout *get_or_create_pipeline_layout( } -/* The fragment shader for the blend->image subpass can be configured to either - * use or not a sampler3d lookup table; however, even if the shader does not use - * the sampler, a valid descriptor set should be bound. Create that here, linked to - * a 1x1x1 image. - */ -static bool init_dummy_images(struct wlr_vk_renderer *renderer) { - VkResult res; - VkDevice dev = renderer->dev->dev; - - VkFormat format = VK_FORMAT_R32G32B32A32_SFLOAT; - - VkImageCreateInfo img_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .imageType = VK_IMAGE_TYPE_3D, - .format = format, - .mipLevels = 1, - .arrayLayers = 1, - .samples = VK_SAMPLE_COUNT_1_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .extent = (VkExtent3D) { 1, 1, 1 }, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = VK_IMAGE_USAGE_SAMPLED_BIT, - }; - res = vkCreateImage(dev, &img_info, NULL, &renderer->dummy3d_image); - if (res != VK_SUCCESS) { - wlr_vk_error("vkCreateImage failed", res); - return false; - } - - VkMemoryRequirements mem_reqs = {0}; - vkGetImageMemoryRequirements(dev, renderer->dummy3d_image, &mem_reqs); - int mem_type_index = vulkan_find_mem_type(renderer->dev, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mem_reqs.memoryTypeBits); - if (mem_type_index == -1) { - wlr_log(WLR_ERROR, "Failed to find suitable memory type"); - return false; - } - VkMemoryAllocateInfo mem_info = { - .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - .allocationSize = mem_reqs.size, - .memoryTypeIndex = mem_type_index, - }; - res = vkAllocateMemory(dev, &mem_info, NULL, &renderer->dummy3d_mem); - if (res != VK_SUCCESS) { - wlr_vk_error("vkAllocateMemory failed", res); - return false; - } - res = vkBindImageMemory(dev, renderer->dummy3d_image, renderer->dummy3d_mem, 0); - if (res != VK_SUCCESS) { - wlr_vk_error("vkBindMemory failed", res); - return false; - } - - VkImageViewCreateInfo view_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .viewType = VK_IMAGE_VIEW_TYPE_3D, - .format = format, - .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, - .components.g = VK_COMPONENT_SWIZZLE_IDENTITY, - .components.b = VK_COMPONENT_SWIZZLE_IDENTITY, - .components.a = VK_COMPONENT_SWIZZLE_IDENTITY, - .subresourceRange = (VkImageSubresourceRange) { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - .image = renderer->dummy3d_image, - }; - res = vkCreateImageView(dev, &view_info, NULL, &renderer->dummy3d_image_view); - if (res != VK_SUCCESS) { - wlr_vk_error("vkCreateImageView failed", res); - return false; - } - - renderer->output_ds_lut3d_dummy_pool = vulkan_alloc_texture_ds(renderer, - renderer->output_ds_lut3d_layout, &renderer->output_ds_lut3d_dummy); - if (!renderer->output_ds_lut3d_dummy_pool) { - wlr_log(WLR_ERROR, "Failed to allocate descriptor"); - return false; - } - VkDescriptorImageInfo ds_img_info = { - .imageView = renderer->dummy3d_image_view, - .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - }; - VkWriteDescriptorSet ds_write = { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .dstSet = renderer->output_ds_lut3d_dummy, - .pImageInfo = &ds_img_info, - }; - vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL); - - return true; -} - // Creates static render data, such as sampler, layouts and shader modules // for the given renderer. // Cleanup is done by destroying the renderer. @@ -2102,10 +1997,6 @@ static bool init_static_render_data(struct wlr_vk_renderer *renderer) { return false; } - if (!init_dummy_images(renderer)) { - return false; - } - // load vert module and tex frag module since they are needed to // initialize the tex pipeline VkShaderModuleCreateInfo sinfo = { @@ -2143,12 +2034,25 @@ static bool init_static_render_data(struct wlr_vk_renderer *renderer) { sinfo = (VkShaderModuleCreateInfo){ .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, - .codeSize = sizeof(output_frag_data), - .pCode = output_frag_data, + .codeSize = sizeof(output_inverse_rgb_data), + .pCode = output_inverse_rgb_data, }; - res = vkCreateShaderModule(dev, &sinfo, NULL, &renderer->output_module); + + res = vkCreateShaderModule(dev, &sinfo, NULL, &renderer->output_module_srgb); if (res != VK_SUCCESS) { - wlr_vk_error("Failed to create blend->output fragment shader module", res); + wlr_vk_error("Failed to create blend->output fragment shader module for srgb", res); + return false; + } + + sinfo = (VkShaderModuleCreateInfo){ + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .codeSize = sizeof(output_lut_3d_data), + .pCode = output_lut_3d_data, + }; + + res = vkCreateShaderModule(dev, &sinfo, NULL, &renderer->output_module_3d_lut); + if (res != VK_SUCCESS) { + wlr_vk_error("Failed to create blend->output fragment shader module for 3d lut", res); return false; } diff --git a/render/vulkan/shaders/meson.build b/render/vulkan/shaders/meson.build index 50f4a1f0d..3b37433fa 100644 --- a/render/vulkan/shaders/meson.build +++ b/render/vulkan/shaders/meson.build @@ -2,7 +2,6 @@ vulkan_shaders_src = [ 'common.vert', 'texture.frag', 'quad.frag', - 'output.frag', ] vulkan_shaders = [] @@ -21,4 +20,24 @@ foreach shader : vulkan_shaders_src vulkan_shaders += [header] endforeach +vulkan_shader_output_color_transforms = { + 'inverse_rgb': '0', + 'lut_3d': '1', +} + +foreach name, ident : vulkan_shader_output_color_transforms + name = 'output_' + name + args = [glslang, '-DOUTPUT_TRANSFORM=' + ident, '-V', '@INPUT@', '-o', '@OUTPUT@', '--vn', name + '_data'] + if glslang_version.version_compare('>=11.0.0') + args += '--quiet' + endif + header = custom_target( + name + '.frag_spv', + output: name + '.frag.h', + input: 'output.frag', + command: args) + + vulkan_shaders += [header] +endforeach + wlr_files += vulkan_shaders diff --git a/render/vulkan/shaders/output.frag b/render/vulkan/shaders/output.frag index 2a744aa24..ece303308 100644 --- a/render/vulkan/shaders/output.frag +++ b/render/vulkan/shaders/output.frag @@ -2,59 +2,51 @@ layout (input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput in_color; -layout(set = 1, binding = 0) uniform sampler3D lut_3d; - -layout(location = 0) in vec2 uv; -layout(location = 0) out vec4 out_color; - -/* struct wlr_vk_frag_output_pcr_data */ -layout(push_constant) uniform UBO { - layout(offset = 80) float lut_3d_offset; - float lut_3d_scale; -} data; - -layout (constant_id = 0) const int OUTPUT_TRANSFORM = 0; - // Matches enum wlr_vk_output_transform #define OUTPUT_TRANSFORM_INVERSE_SRGB 0 #define OUTPUT_TRANSFORM_LUT_3D 1 +layout(location = 0) in vec2 uv; +layout(location = 0) out vec4 out_color; + +#if OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_LUT_3D + layout(set = 1, binding = 0) uniform sampler3D lut_3d; + + /* struct wlr_vk_frag_output_pcr_data */ + layout(push_constant) uniform UBO { + layout(offset = 80) float lut_3d_offset; + float lut_3d_scale; + } data; +#endif + float linear_channel_to_srgb(float x) { return max(min(x * 12.92, 0.04045), 1.055 * pow(x, 1. / 2.4) - 0.055); } -vec4 linear_color_to_srgb(vec4 color) { - if (color.a == 0) { - return vec4(0); - } - color.rgb /= color.a; - color.rgb = vec3( - linear_channel_to_srgb(color.r), - linear_channel_to_srgb(color.g), - linear_channel_to_srgb(color.b) - ); - color.rgb *= color.a; - return color; -} - void main() { vec4 val = subpassLoad(in_color).rgba; - if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_LUT_3D) { - if (val.a == 0) { - out_color = vec4(0); - return; - } - // Convert from pre-multiplied alpha to straight alpha - vec3 rgb = val.rgb / val.a; - - // Apply 3D LUT - vec3 pos = data.lut_3d_offset + rgb * data.lut_3d_scale; - rgb = texture(lut_3d, pos).rgb; - - // Back to pre-multiplied alpha - out_color = vec4(rgb * val.a, val.a); - } else { // OUTPUT_TRANSFORM_INVERSE_SRGB - // Produce post-premultiplied sRGB encoded values - out_color = linear_color_to_srgb(val); + if (val.a == 0) { + out_color = vec4(0); + return; } + + // Convert from pre-multiplied alpha to straight alpha + vec3 rgb = val.rgb / val.a; + +#if OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_LUT_3D + // Apply 3D LUT + vec3 pos = data.lut_3d_offset + rgb * data.lut_3d_scale; + rgb = texture(lut_3d, pos).rgb; +#endif + +#if OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_INVERSE_SRGB + rgb = vec3( + linear_channel_to_srgb(rgb.r), + linear_channel_to_srgb(rgb.g), + linear_channel_to_srgb(rgb.b) + ); +#endif + + // Back to pre-multiplied alpha + out_color = vec4(rgb * val.a, val.a); }