Quake II RTX doxygen  1.0 dev
tone_mapping.c File Reference
#include "vkpt.h"

Go to the source code of this file.

Macros

#define BARRIER_COMPUTE(cmd_buf, img)
 

Enumerations

enum  { TONE_MAPPING_HISTOGRAM, TONE_MAPPING_CURVE, TONE_MAPPING_APPLY, TM_NUM_PIPELINES }
 

Functions

VkResult vkpt_tone_mapping_initialize ()
 
VkResult vkpt_tone_mapping_destroy ()
 
void vkpt_tone_mapping_request_reset ()
 
VkResult vkpt_tone_mapping_create_pipelines ()
 
VkResult vkpt_tone_mapping_reset (VkCommandBuffer cmd_buf)
 
VkResult vkpt_tone_mapping_destroy_pipelines ()
 
VkResult vkpt_tone_mapping_record_cmd_buffer (VkCommandBuffer cmd_buf, float frame_time)
 

Variables

static VkPipeline pipelines [TM_NUM_PIPELINES]
 
static VkPipelineLayout pipeline_layout_tone_mapping_histogram
 
static VkPipelineLayout pipeline_layout_tone_mapping_curve
 
static VkPipelineLayout pipeline_layout_tone_mapping_apply
 
static int reset_required = 1
 

Macro Definition Documentation

◆ BARRIER_COMPUTE

#define BARRIER_COMPUTE (   cmd_buf,
  img 
)
Value:
do { \
VkImageSubresourceRange subresource_range = { \
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, \
.baseMipLevel = 0, \
.levelCount = 1, \
.baseArrayLayer = 0, \
.layerCount = 1 \
}; \
IMAGE_BARRIER(cmd_buf, \
.image = img, \
.subresourceRange = subresource_range, \
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, \
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT, \
.oldLayout = VK_IMAGE_LAYOUT_GENERAL, \
.newLayout = VK_IMAGE_LAYOUT_GENERAL, \
); \
} while(0)

Definition at line 218 of file tone_mapping.c.

Enumeration Type Documentation

◆ anonymous enum

anonymous enum
Enumerator
TONE_MAPPING_HISTOGRAM 
TONE_MAPPING_CURVE 
TONE_MAPPING_APPLY 
TM_NUM_PIPELINES 

Definition at line 60 of file tone_mapping.c.

Function Documentation

◆ vkpt_tone_mapping_create_pipelines()

VkResult vkpt_tone_mapping_create_pipelines ( )

Definition at line 146 of file tone_mapping.c.

147 {
148  VkComputePipelineCreateInfo pipeline_info[TM_NUM_PIPELINES] = {
150  .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
151  .stage = SHADER_STAGE(QVK_MOD_TONE_MAPPING_HISTOGRAM_COMP, VK_SHADER_STAGE_COMPUTE_BIT),
153  },
154  [TONE_MAPPING_CURVE] = {
155  .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
156  .stage = SHADER_STAGE(QVK_MOD_TONE_MAPPING_CURVE_COMP, VK_SHADER_STAGE_COMPUTE_BIT),
158  },
159  [TONE_MAPPING_APPLY] = {
160  .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
161  .stage = SHADER_STAGE(QVK_MOD_TONE_MAPPING_APPLY_COMP, VK_SHADER_STAGE_COMPUTE_BIT),
163  },
164  };
165 
166  _VK(vkCreateComputePipelines(qvk.device, 0, LENGTH(pipeline_info), pipeline_info, 0, pipelines));
167 
168  reset_required = 1;
169 
170  return VK_SUCCESS;
171 }

◆ vkpt_tone_mapping_destroy()

VkResult vkpt_tone_mapping_destroy ( )

Definition at line 126 of file tone_mapping.c.

127 {
128  vkDestroyPipelineLayout(qvk.device, pipeline_layout_tone_mapping_histogram, NULL);
129  vkDestroyPipelineLayout(qvk.device, pipeline_layout_tone_mapping_curve, NULL);
130  vkDestroyPipelineLayout(qvk.device, pipeline_layout_tone_mapping_apply, NULL);
131  return VK_SUCCESS;
132 }

◆ vkpt_tone_mapping_destroy_pipelines()

VkResult vkpt_tone_mapping_destroy_pipelines ( )

Definition at line 207 of file tone_mapping.c.

208 {
209  for (int i = 0; i < TM_NUM_PIPELINES; i++)
210  vkDestroyPipeline(qvk.device, pipelines[i], NULL);
211  return VK_SUCCESS;
212 }

◆ vkpt_tone_mapping_initialize()

VkResult vkpt_tone_mapping_initialize ( )

Definition at line 76 of file tone_mapping.c.

77 {
78  VkDescriptorSetLayout desc_set_layouts[] = {
82  };
83 
84  VkPushConstantRange push_constant_range_curve = {
85  .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
86  .offset = 0,
87  .size = 16*sizeof(float)
88  };
89 
90  VkPushConstantRange push_constant_range_apply = {
91  .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
92  .offset = 0,
93  .size = 3*sizeof(float)
94  };
95 
97  .setLayoutCount = LENGTH(desc_set_layouts),
98  .pSetLayouts = desc_set_layouts,
99  .pushConstantRangeCount = 0,
100  .pPushConstantRanges = NULL
101  );
103 
105  .setLayoutCount = LENGTH(desc_set_layouts),
106  .pSetLayouts = desc_set_layouts,
107  .pushConstantRangeCount = 1,
108  .pPushConstantRanges = &push_constant_range_curve
109  );
111 
113  .setLayoutCount = LENGTH(desc_set_layouts),
114  .pSetLayouts = desc_set_layouts,
115  .pushConstantRangeCount = 1,
116  .pPushConstantRanges = &push_constant_range_apply
117  );
119 
120  return VK_SUCCESS;
121 }

◆ vkpt_tone_mapping_record_cmd_buffer()

VkResult vkpt_tone_mapping_record_cmd_buffer ( VkCommandBuffer  cmd_buf,
float  frame_time 
)

Definition at line 241 of file tone_mapping.c.

242 {
243  if (reset_required)
244  {
245  // Clear the histogram image.
246  vkpt_tone_mapping_reset(cmd_buf);
247  }
248 
249  VkDescriptorSet desc_sets[] = {
253  };
254  BARRIER_COMPUTE(cmd_buf, qvk.images[VKPT_IMG_TAA_OUTPUT]);
255 
256 
257  // Record instructions to run the compute shader that updates the histogram.
258  vkCmdBindPipeline(cmd_buf, VK_PIPELINE_BIND_POINT_COMPUTE, pipelines[TONE_MAPPING_HISTOGRAM]);
259  vkCmdBindDescriptorSets(cmd_buf, VK_PIPELINE_BIND_POINT_COMPUTE,
260  pipeline_layout_tone_mapping_histogram, 0, LENGTH(desc_sets), desc_sets, 0, 0);
261 
262  vkCmdDispatch(cmd_buf,
263  (qvk.extent_render.width + 15) / 16,
264  (qvk.extent_render.height + 15) / 16,
265  1);
266 
267  BUFFER_BARRIER(cmd_buf,
268  .buffer = qvk.buf_tonemap.buffer,
269  .offset = 0,
270  .size = VK_WHOLE_SIZE,
271  .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
272  .dstAccessMask = VK_ACCESS_SHADER_READ_BIT
273  );
274 
275 
276  // Record instructions to run the compute shader that computes the tone
277  // curve and autoexposure constants from the histogram.
278  vkCmdBindPipeline(cmd_buf, VK_PIPELINE_BIND_POINT_COMPUTE, pipelines[TONE_MAPPING_CURVE]);
279  vkCmdBindDescriptorSets(cmd_buf, VK_PIPELINE_BIND_POINT_COMPUTE,
280  pipeline_layout_tone_mapping_curve, 0, LENGTH(desc_sets), desc_sets, 0, 0);
281 
282  // Compute the push constants for the tone curve generation shader:
283  // whether to ignore previous tone curve results, how much to blend this
284  // frame's tone curve with the previous frame's tone curve, and the kernel
285  // used to filter the tone curve's slopes.
286  // This is one of the things we added to Eilertsen's tone mapper, and helps
287  // prevent artifacts that occur when the tone curve's slopes become flat or
288  // change too suddenly (much like when you specify a curve in an image
289  // processing application that is too intense). This especially helps on
290  // shadow edges in some scenes.
291  // In addition, we assume the kernel is symmetric; this allows us to only
292  // specify half of it in our push constant buffer.
293 
294  // Note that the second argument of Cvar_Get only specifies the default
295  // value in code if none is set; the value of tm_slope_blur_sigma specified
296  // in global_ubo.h will override this.
297  float slope_blur_sigma = Cvar_Get("tm_slope_blur_sigma", "6.0", 0)->value;
298  float push_constants_tm2_curve[16] = {
299  reset_required ? 1.0 : 0.0, // 1 means reset the histogram
300  frame_time, // Frame time
301  0.0, 0.0, 0.0, 0.0, // Slope kernel filter
302  0.0, 0.0, 0.0, 0.0,
303  0.0, 0.0, 0.0, 0.0,
304  0.0, 0.0
305  };
306 
307  // Compute Gaussian curve and sum, taking symmetry into account.
308  float gaussian_sum = 0.0;
309  for (int i = 0; i < 14; ++i)
310  {
311  float kernel_value = exp(-i * i / (2.0 * slope_blur_sigma * slope_blur_sigma));
312  gaussian_sum += kernel_value * (i == 0 ? 1 : 2);
313  push_constants_tm2_curve[i + 2] = kernel_value;
314  }
315  // Normalize the result (since even with an analytic normalization factor,
316  // the results may not sum to one).
317  for (int i = 0; i < 14; ++i) {
318  push_constants_tm2_curve[i + 2] /= gaussian_sum;
319  }
320 
321  vkCmdPushConstants(cmd_buf, pipeline_layout_tone_mapping_curve,
322  VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(push_constants_tm2_curve), push_constants_tm2_curve);
323 
324  vkCmdDispatch(cmd_buf, 1, 1, 1);
325 
326  BUFFER_BARRIER(cmd_buf,
327  .buffer = qvk.buf_tonemap.buffer,
328  .offset = 0,
329  .size = VK_WHOLE_SIZE,
330  .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
331  .dstAccessMask = VK_ACCESS_SHADER_READ_BIT
332  );
333 
334 
335  // Record instructions to apply our tone curve to the final image, apply
336  // the autoexposure tone mapper to the final image, and blend the results
337  // of the two techniques.
338  vkCmdBindPipeline(cmd_buf, VK_PIPELINE_BIND_POINT_COMPUTE, pipelines[TONE_MAPPING_APPLY]);
339  vkCmdBindDescriptorSets(cmd_buf, VK_PIPELINE_BIND_POINT_COMPUTE,
340  pipeline_layout_tone_mapping_apply, 0, LENGTH(desc_sets), desc_sets, 0, 0);
341 
342  // At the end of the hue-preserving tone mapper, the luminance of every
343  // pixel is mapped to the range [0,1]. However, because this tone
344  // mapper adjusts luminance while preserving hue and saturation, the values
345  // of some RGB channels may lie outside [0,1]. To finish off the tone
346  // mapping pipeline and since we want the brightest colors in the scene to
347  // be desaturated a bit for display, we apply a subtle user-configurable
348  // Reinhard tone mapping curve to the brighest values in the image at this
349  // point, preserving pixels with luminance below tm_knee_start.
350  //
351  // If we wanted to support an arbitrary SDR color grading pipeline here or
352  // implement an additional filmic tone mapping pass, for instance, this is
353  // roughly around where it would be applied. For applications that need to
354  // output both SDR and HDR images but for which creating custom grades
355  // for each format is impractical, one common approach is to
356  // (roughly) use the HDR->SDR transformation to map an SDR color grading
357  // function back to an HDR color grading function.
358 
359  // Defines the white point and where we switch from an identity transform
360  // to a Reinhard transform in the additional tone mapper we apply at the
361  // end of the previous tone mapping pipeline.
362  // Must be between 0 and 1; pixels with luminances above this value have
363  // their RGB values slowly clamped to 1, up to tm_white_point.
364  float knee_start = Cvar_Get("tm_knee_start", "0.9", 0)->value;
365  // Should be greater than 1; defines those RGB values that get mapped to 1.
366  float knee_white_point = Cvar_Get("tm_white_point", "10.0", 0)->value;
367 
368  // We modify Reinhard to smoothly blend with the identity transform up to tm_knee_start.
369  // We need to find w, a, and b such that in y(x) = (wx+a)/(x+b),
370  // * y(knee_start) = tm_knee_start
371  // * dy/dx(knee_start) = 1
372  // * y(knee_white_point) = tm_white_point.
373  // The solution is as follows:
374  float knee_w = (knee_start*(knee_start - 2.0) + knee_white_point) / (knee_white_point - 1.0);
375  float knee_a = -knee_start * knee_start;
376  float knee_b = knee_w - 2.0*knee_start;
377 
378  float push_constants_tm2_apply[3] = {
379  knee_w, // knee_w in piecewise knee adjustment
380  knee_a, // knee_a in piecewise knee adjustment
381  knee_b, // knee_b in piecewise knee adjustment
382  };
383 
384  vkCmdPushConstants(cmd_buf, pipeline_layout_tone_mapping_apply,
385  VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(push_constants_tm2_apply), push_constants_tm2_apply);
386 
387  vkCmdDispatch(cmd_buf,
388  (qvk.extent_render.width + 15) / 16,
389  (qvk.extent_render.height + 15) / 16,
390  1);
391 
392  // Because VKPT_IMG_TAA_OUTPUT changed, we make sure to wait for the image
393  // to be written before continuing. This could be ensured in several
394  // other ways as well.
395  BARRIER_COMPUTE(cmd_buf, qvk.images[VKPT_IMG_TAA_OUTPUT]);
396 
397  reset_required = 0;
398 
399  return VK_SUCCESS;
400 }

Referenced by R_RenderFrame_RTX().

◆ vkpt_tone_mapping_request_reset()

void vkpt_tone_mapping_request_reset ( )

Definition at line 138 of file tone_mapping.c.

139 {
140  reset_required = 1;
141 }

Referenced by R_BeginRegistration_RTX().

◆ vkpt_tone_mapping_reset()

VkResult vkpt_tone_mapping_reset ( VkCommandBuffer  cmd_buf)

Definition at line 176 of file tone_mapping.c.

177 {
178  VkClearColorValue clear_histogram = {
179  .uint32 = { 0, 0, 0, 0 }
180  };
181 
182  BUFFER_BARRIER(cmd_buf,
183  .buffer = qvk.buf_tonemap.buffer,
184  .offset = 0,
185  .size = VK_WHOLE_SIZE,
186  .srcAccessMask = 0,
187  .dstAccessMask = 0
188  );
189 
190  vkCmdFillBuffer(cmd_buf, qvk.buf_tonemap.buffer,
191  0, VK_WHOLE_SIZE, 0);
192 
193  BUFFER_BARRIER(cmd_buf,
194  .buffer = qvk.buf_tonemap.buffer,
195  .offset = 0,
196  .size = VK_WHOLE_SIZE,
197  .srcAccessMask = 0,
198  .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT
199  );
200 
201  return VK_SUCCESS;
202 }

Referenced by vkpt_tone_mapping_record_cmd_buffer().

Variable Documentation

◆ pipeline_layout_tone_mapping_apply

VkPipelineLayout pipeline_layout_tone_mapping_apply
static

◆ pipeline_layout_tone_mapping_curve

VkPipelineLayout pipeline_layout_tone_mapping_curve
static

◆ pipeline_layout_tone_mapping_histogram

VkPipelineLayout pipeline_layout_tone_mapping_histogram
static

◆ pipelines

◆ reset_required

QVK_s::desc_set_ubo
VkDescriptorSet desc_set_ubo
Definition: vkpt.h:226
BUFFER_BARRIER
#define BUFFER_BARRIER(cmd_buf,...)
Definition: vk_util.h:68
TONE_MAPPING_APPLY
@ TONE_MAPPING_APPLY
Definition: tone_mapping.c:63
CREATE_PIPELINE_LAYOUT
#define CREATE_PIPELINE_LAYOUT(dev, layout,...)
Definition: vk_util.h:82
QVK_s::buf_tonemap
BufferResource_t buf_tonemap
Definition: vkpt.h:252
QVK_s::device
VkDevice device
Definition: vkpt.h:172
pipeline_layout_tone_mapping_histogram
static VkPipelineLayout pipeline_layout_tone_mapping_histogram
Definition: tone_mapping.c:68
TONE_MAPPING_CURVE
@ TONE_MAPPING_CURVE
Definition: tone_mapping.c:62
Cvar_Get
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags)
Definition: cvar.c:257
pipelines
static VkPipeline pipelines[TM_NUM_PIPELINES]
Definition: tone_mapping.c:67
_VK
#define _VK(...)
Definition: vkpt.h:65
reset_required
static int reset_required
Definition: tone_mapping.c:71
QVK_s::desc_set_vertex_buffer
VkDescriptorSet desc_set_vertex_buffer
Definition: vkpt.h:241
pipeline_layout_tone_mapping_curve
static VkPipelineLayout pipeline_layout_tone_mapping_curve
Definition: tone_mapping.c:69
BufferResource_s::buffer
VkBuffer buffer
Definition: vk_util.h:34
QVK_s::images
VkImage images[NUM_VKPT_IMAGES]
Definition: vkpt.h:231
LENGTH
#define LENGTH(a)
Definition: tent.c:228
qvk
QVK_t qvk
Definition: main.c:377
TONE_MAPPING_HISTOGRAM
@ TONE_MAPPING_HISTOGRAM
Definition: tone_mapping.c:61
SHADER_STAGE
#define SHADER_STAGE(_module, _stage)
Definition: vkpt.h:116
QVK_s::extent_render
VkExtent2D extent_render
Definition: vkpt.h:184
TM_NUM_PIPELINES
@ TM_NUM_PIPELINES
Definition: tone_mapping.c:64
vkpt_tone_mapping_reset
VkResult vkpt_tone_mapping_reset(VkCommandBuffer cmd_buf)
Definition: tone_mapping.c:176
ATTACH_LABEL_VARIABLE
#define ATTACH_LABEL_VARIABLE(a, type)
Definition: vk_util.h:137
QVK_s::desc_set_layout_textures
VkDescriptorSetLayout desc_set_layout_textures
Definition: vkpt.h:228
QVK_s::desc_set_layout_ubo
VkDescriptorSetLayout desc_set_layout_ubo
Definition: vkpt.h:225
pipeline_layout_tone_mapping_apply
static VkPipelineLayout pipeline_layout_tone_mapping_apply
Definition: tone_mapping.c:70
BARRIER_COMPUTE
#define BARRIER_COMPUTE(cmd_buf, img)
Definition: tone_mapping.c:218
qvk_get_current_desc_set_textures
VkDescriptorSet qvk_get_current_desc_set_textures()
Definition: main.c:1847
QVK_s::desc_set_layout_vertex_buffer
VkDescriptorSetLayout desc_set_layout_vertex_buffer
Definition: vkpt.h:240