diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 8b7368660..392c2de5e 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -162,6 +162,7 @@ enum wlr_vk_shader_source { enum wlr_vk_output_transform { WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB = 0, WLR_VK_OUTPUT_TRANSFORM_LUT3D = 1, + WLR_VK_OUTPUT_TRANSFORM_LUT3x1D = 2, }; struct wlr_vk_pipeline_key { @@ -192,6 +193,7 @@ struct wlr_vk_render_format_setup { VkPipeline output_pipe_srgb; VkPipeline output_pipe_lut3d; + VkPipeline output_pipe_lut3x1d; struct wlr_vk_renderer *renderer; struct wl_list pipelines; // struct wlr_vk_pipeline.link @@ -270,6 +272,7 @@ struct wlr_vk_renderer { VkShaderModule quad_frag_module; VkShaderModule output_module_srgb; VkShaderModule output_module_3d_lut; + VkShaderModule output_module_3x1d_lut; struct wl_list pipeline_layouts; // struct wlr_vk_pipeline_layout.link diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index e9f7280e0..45fe37110 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -146,6 +146,23 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { break; } + case COLOR_TRANSFORM_LUT_3x1D: { + pl = render_buffer->plain.render_setup->output_pipe_lut3x1d; + struct wlr_vk_color_transform *transform = + get_color_transform(pass->color_transform, renderer); + ds[ds_len++] = transform->lut.ds; + + struct wlr_color_transform_lut3x1d *lut = + wlr_color_transform_lut3x1d_from_base(pass->color_transform); + struct wlr_vk_frag_output_pcr_data frag_pcr_data = { + .lut_3d_offset = 0.5f / lut->ramp_size, + .lut_3d_scale = (float)(lut->ramp_size - 1) / lut->ramp_size, + }; + 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; } @@ -889,6 +906,166 @@ fail_image: return false; } +static bool create_3x1d_lut_image(struct wlr_vk_renderer *renderer, + const struct wlr_color_transform_lut3x1d *lut, + VkImage *image, VkImageView *image_view, + VkDeviceMemory *memory, VkDescriptorSet *ds, + struct wlr_vk_descriptor_pool **ds_pool) { + VkDevice dev = renderer->dev->dev; + VkResult res; + + *image = VK_NULL_HANDLE; + *memory = VK_NULL_HANDLE; + *image_view = VK_NULL_HANDLE; + *ds = VK_NULL_HANDLE; + *ds_pool = NULL; + + // R32G32B32 is not a required Vulkan format + // TODO: use it when available + VkFormat format = VK_FORMAT_R32G32B32A32_SFLOAT; + + VkImageCreateInfo img_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_1D, + .format = format, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .extent = (VkExtent3D) { lut->ramp_size, 1, 1 }, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, + }; + res = vkCreateImage(dev, &img_info, NULL, image); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateImage failed", res); + return NULL; + } + + VkMemoryRequirements mem_reqs = {0}; + vkGetImageMemoryRequirements(dev, *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"); + goto fail_image; + } + + VkMemoryAllocateInfo mem_info = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = mem_reqs.size, + .memoryTypeIndex = mem_type_index, + }; + res = vkAllocateMemory(dev, &mem_info, NULL, memory); + if (res != VK_SUCCESS) { + wlr_vk_error("vkAllocateMemory failed", res); + goto fail_image; + } + + res = vkBindImageMemory(dev, *image, *memory, 0); + if (res != VK_SUCCESS) { + wlr_vk_error("vkBindMemory failed", res); + goto fail_memory; + } + + VkImageViewCreateInfo view_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .viewType = VK_IMAGE_VIEW_TYPE_1D, + .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 = *image, + }; + res = vkCreateImageView(dev, &view_info, NULL, image_view); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateImageView failed", res); + goto fail_image; + } + + size_t bytes_per_block = 4 * sizeof(float); + size_t size = lut->ramp_size * bytes_per_block; + struct wlr_vk_buffer_span span = vulkan_get_stage_span(renderer, + size, bytes_per_block); + if (!span.buffer || span.alloc.size != size) { + wlr_log(WLR_ERROR, "Failed to retrieve staging buffer"); + goto fail_imageview; + } + + char *map = (char*)span.buffer->cpu_mapping + span.alloc.start; + float *dst = (float*)map; + + float normalize = (1 << 16) - 1; + for (size_t i = 0; i < lut->ramp_size; i++) { + size_t dst_offset = 4 * i; + dst[dst_offset + 0] = (float)lut->r[i] / normalize; + dst[dst_offset + 1] = (float)lut->g[i] / normalize; + dst[dst_offset + 2] = (float)lut->b[i] / normalize; + dst[dst_offset + 3] = 1.0; + } + + VkCommandBuffer cb = vulkan_record_stage_cb(renderer); + vulkan_change_layout(cb, *image, + VK_IMAGE_LAYOUT_UNDEFINED, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT); + VkBufferImageCopy copy = { + .bufferOffset = span.alloc.start, + .imageExtent.width = lut->ramp_size, + .imageExtent.height = 1, + .imageExtent.depth = 1, + .imageSubresource.layerCount = 1, + .imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + }; + vkCmdCopyBufferToImage(cb, span.buffer->buffer, *image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©); + vulkan_change_layout(cb, *image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_ACCESS_SHADER_READ_BIT); + + *ds_pool = vulkan_alloc_texture_ds(renderer, + renderer->output_ds_lut3d_layout, ds); + if (!*ds_pool) { + wlr_log(WLR_ERROR, "Failed to allocate descriptor"); + goto fail_imageview; + } + + VkDescriptorImageInfo ds_img_info = { + .imageView = *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 = *ds, + .pImageInfo = &ds_img_info, + }; + vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL); + + return true; + +fail_imageview: + vkDestroyImageView(dev, *image_view, NULL); +fail_memory: + vkFreeMemory(dev, *memory, NULL); +fail_image: + vkDestroyImage(dev, *image, NULL); + return false; +} + static struct wlr_vk_color_transform *vk_color_transform_create( struct wlr_vk_renderer *renderer, struct wlr_color_transform *transform) { struct wlr_vk_color_transform *vk_transform = @@ -897,7 +1074,8 @@ static struct wlr_vk_color_transform *vk_color_transform_create( return NULL; } - if (transform->type == COLOR_TRANSFORM_LUT_3D) { + switch(transform->type) { + case COLOR_TRANSFORM_LUT_3D: if (!create_3d_lut_image(renderer, wlr_color_transform_lut3d_from_base(transform), &vk_transform->lut.image, @@ -908,6 +1086,21 @@ static struct wlr_vk_color_transform *vk_color_transform_create( free(vk_transform); return NULL; } + break; + case COLOR_TRANSFORM_LUT_3x1D: + if (!create_3x1d_lut_image(renderer, + wlr_color_transform_lut3x1d_from_base(transform), + &vk_transform->lut.image, + &vk_transform->lut.image_view, + &vk_transform->lut.memory, + &vk_transform->lut.ds, + &vk_transform->lut.ds_pool)) { + free(vk_transform); + return NULL; + } + break; + case COLOR_TRANSFORM_SRGB: + break; } wlr_addon_init(&vk_transform->addon, &transform->addons, diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 8c51a8ae9..5cb3612cf 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -26,6 +26,7 @@ #include "render/vulkan/shaders/quad.frag.h" #include "render/vulkan/shaders/output_inverse_rgb.frag.h" #include "render/vulkan/shaders/output_lut_3d.frag.h" +#include "render/vulkan/shaders/output_lut_3x1d.frag.h" #include "types/wlr_buffer.h" #include "types/wlr_matrix.h" @@ -1120,6 +1121,7 @@ static void vulkan_destroy(struct wlr_renderer *wlr_renderer) { vkDestroyShaderModule(dev->dev, renderer->quad_frag_module, NULL); vkDestroyShaderModule(dev->dev, renderer->output_module_srgb, NULL); vkDestroyShaderModule(dev->dev, renderer->output_module_3d_lut, NULL); + vkDestroyShaderModule(dev->dev, renderer->output_module_3x1d_lut, NULL); struct wlr_vk_pipeline_layout *pipeline_layout, *pipeline_layout_tmp; wl_list_for_each_safe(pipeline_layout, pipeline_layout_tmp, @@ -1803,6 +1805,9 @@ static bool init_blend_to_output_pipeline(struct wlr_vk_renderer *renderer, case WLR_VK_OUTPUT_TRANSFORM_LUT3D: output_module = renderer->output_module_3d_lut; break; + case WLR_VK_OUTPUT_TRANSFORM_LUT3x1D: + output_module = renderer->output_module_3x1d_lut; + break; } VkPipelineShaderStageCreateInfo tex_stages[2] = { @@ -2053,6 +2058,18 @@ static bool init_static_render_data(struct wlr_vk_renderer *renderer) { return false; } + sinfo = (VkShaderModuleCreateInfo){ + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .codeSize = sizeof(output_lut_3x1d_data), + .pCode = output_lut_3x1d_data, + }; + + res = vkCreateShaderModule(dev, &sinfo, NULL, &renderer->output_module_3x1d_lut); + if (res != VK_SUCCESS) { + wlr_vk_error("Failed to create blend->output fragment shader module for 3x1d lut", res); + return false; + } + return true; } @@ -2202,6 +2219,11 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( &setup->output_pipe_srgb, WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB)) { goto error; } + if (!init_blend_to_output_pipeline( + renderer, setup->render_pass, renderer->output_pipe_layout, + &setup->output_pipe_lut3x1d, WLR_VK_OUTPUT_TRANSFORM_LUT3x1D)) { + goto error; + } } else { assert(format->vk_srgb); VkAttachmentDescription attachment = { diff --git a/render/vulkan/shaders/meson.build b/render/vulkan/shaders/meson.build index 3b37433fa..81db20019 100644 --- a/render/vulkan/shaders/meson.build +++ b/render/vulkan/shaders/meson.build @@ -23,6 +23,7 @@ endforeach vulkan_shader_output_color_transforms = { 'inverse_rgb': '0', 'lut_3d': '1', + 'lut_3x1d': '2', } foreach name, ident : vulkan_shader_output_color_transforms diff --git a/render/vulkan/shaders/output.frag b/render/vulkan/shaders/output.frag index ece303308..56fea0f09 100644 --- a/render/vulkan/shaders/output.frag +++ b/render/vulkan/shaders/output.frag @@ -5,13 +5,20 @@ layout (input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput i // Matches enum wlr_vk_output_transform #define OUTPUT_TRANSFORM_INVERSE_SRGB 0 #define OUTPUT_TRANSFORM_LUT_3D 1 +#define OUTPUT_TRANSFORM_LUT_3x1D 2 layout(location = 0) in vec2 uv; layout(location = 0) out vec4 out_color; +#if OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_LUT_3x1D + layout(set = 1, binding = 0) uniform sampler1D lut_3x1d; +#endif + #if OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_LUT_3D layout(set = 1, binding = 0) uniform sampler3D lut_3d; +#endif +#if OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_LUT_3D || OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_LUT_3x1D /* struct wlr_vk_frag_output_pcr_data */ layout(push_constant) uniform UBO { layout(offset = 80) float lut_3d_offset; @@ -39,6 +46,15 @@ void main() { rgb = texture(lut_3d, pos).rgb; #endif +#if OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_LUT_3x1D + vec3 pos = data.lut_3d_offset + rgb * data.lut_3d_scale; + rgb = vec3( + texture(lut_3x1d, pos.r).r, + texture(lut_3x1d, pos.g).g, + texture(lut_3x1d, pos.b).b + ); +#endif + #if OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_INVERSE_SRGB rgb = vec3( linear_channel_to_srgb(rgb.r),