1/**************************************************************************/
2/* luminance.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "luminance.h"
32#include "../framebuffer_cache_rd.h"
33#include "../uniform_set_cache_rd.h"
34#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
35
36using namespace RendererRD;
37
38Luminance::Luminance(bool p_prefer_raster_effects) {
39 prefer_raster_effects = p_prefer_raster_effects;
40
41 if (prefer_raster_effects) {
42 Vector<String> luminance_reduce_modes;
43 luminance_reduce_modes.push_back("\n#define FIRST_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FIRST
44 luminance_reduce_modes.push_back("\n"); // LUMINANCE_REDUCE_FRAGMENT
45 luminance_reduce_modes.push_back("\n#define FINAL_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FINAL
46
47 luminance_reduce_raster.shader.initialize(luminance_reduce_modes);
48 luminance_reduce_raster.shader_version = luminance_reduce_raster.shader.version_create();
49
50 for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) {
51 luminance_reduce_raster.pipelines[i].setup(luminance_reduce_raster.shader.version_get_shader(luminance_reduce_raster.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
52 }
53 } else {
54 // Initialize luminance_reduce
55 Vector<String> luminance_reduce_modes;
56 luminance_reduce_modes.push_back("\n#define READ_TEXTURE\n");
57 luminance_reduce_modes.push_back("\n");
58 luminance_reduce_modes.push_back("\n#define WRITE_LUMINANCE\n");
59
60 luminance_reduce.shader.initialize(luminance_reduce_modes);
61 luminance_reduce.shader_version = luminance_reduce.shader.version_create();
62
63 for (int i = 0; i < LUMINANCE_REDUCE_MAX; i++) {
64 luminance_reduce.pipelines[i] = RD::get_singleton()->compute_pipeline_create(luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, i));
65 }
66
67 for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) {
68 luminance_reduce_raster.pipelines[i].clear();
69 }
70 }
71}
72
73Luminance::~Luminance() {
74 if (prefer_raster_effects) {
75 luminance_reduce_raster.shader.version_free(luminance_reduce_raster.shader_version);
76 } else {
77 luminance_reduce.shader.version_free(luminance_reduce.shader_version);
78 }
79}
80
81void Luminance::LuminanceBuffers::set_prefer_raster_effects(bool p_prefer_raster_effects) {
82 prefer_raster_effects = p_prefer_raster_effects;
83}
84
85void Luminance::LuminanceBuffers::configure(RenderSceneBuffersRD *p_render_buffers) {
86 Size2i internal_size = p_render_buffers->get_internal_size();
87 int w = internal_size.x;
88 int h = internal_size.y;
89
90 while (true) {
91 w = MAX(w / 8, 1);
92 h = MAX(h / 8, 1);
93
94 RD::TextureFormat tf;
95 tf.format = RD::DATA_FORMAT_R32_SFLOAT;
96 tf.width = w;
97 tf.height = h;
98
99 bool final = w == 1 && h == 1;
100
101 if (prefer_raster_effects) {
102 tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
103 } else {
104 tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT;
105 if (final) {
106 tf.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT;
107 }
108 }
109
110 RID texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
111 reduce.push_back(texture);
112
113 if (final) {
114 current = RD::get_singleton()->texture_create(tf, RD::TextureView());
115 break;
116 }
117 }
118}
119
120void Luminance::LuminanceBuffers::free_data() {
121 for (int i = 0; i < reduce.size(); i++) {
122 RD::get_singleton()->free(reduce[i]);
123 }
124 reduce.clear();
125
126 if (current.is_valid()) {
127 RD::get_singleton()->free(current);
128 current = RID();
129 }
130}
131
132Ref<Luminance::LuminanceBuffers> Luminance::get_luminance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers) {
133 if (p_render_buffers->has_custom_data(RB_LUMINANCE_BUFFERS)) {
134 return p_render_buffers->get_custom_data(RB_LUMINANCE_BUFFERS);
135 }
136
137 Ref<LuminanceBuffers> buffers;
138 buffers.instantiate();
139 buffers->set_prefer_raster_effects(prefer_raster_effects);
140 buffers->configure(p_render_buffers.ptr());
141
142 p_render_buffers->set_custom_data(RB_LUMINANCE_BUFFERS, buffers);
143
144 return buffers;
145}
146
147RID Luminance::get_current_luminance_buffer(Ref<RenderSceneBuffersRD> p_render_buffers) {
148 if (p_render_buffers->has_custom_data(RB_LUMINANCE_BUFFERS)) {
149 Ref<LuminanceBuffers> buffers = p_render_buffers->get_custom_data(RB_LUMINANCE_BUFFERS);
150 return buffers->current;
151 }
152
153 return RID();
154}
155
156void Luminance::luminance_reduction(RID p_source_texture, const Size2i p_source_size, Ref<LuminanceBuffers> p_luminance_buffers, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set) {
157 UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
158 ERR_FAIL_NULL(uniform_set_cache);
159 MaterialStorage *material_storage = MaterialStorage::get_singleton();
160 ERR_FAIL_NULL(material_storage);
161
162 // setup our uniforms
163 RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
164
165 if (prefer_raster_effects) {
166 LuminanceReduceRasterPushConstant push_constant;
167 memset(&push_constant, 0, sizeof(LuminanceReduceRasterPushConstant));
168
169 push_constant.max_luminance = p_max_luminance;
170 push_constant.min_luminance = p_min_luminance;
171 push_constant.exposure_adjust = p_adjust;
172
173 for (int i = 0; i < p_luminance_buffers->reduce.size(); i++) {
174 push_constant.source_size[0] = i == 0 ? p_source_size.x : push_constant.dest_size[0];
175 push_constant.source_size[1] = i == 0 ? p_source_size.y : push_constant.dest_size[1];
176 push_constant.dest_size[0] = MAX(push_constant.source_size[0] / 8, 1);
177 push_constant.dest_size[1] = MAX(push_constant.source_size[1] / 8, 1);
178
179 bool final = !p_set && (push_constant.dest_size[0] == 1) && (push_constant.dest_size[1] == 1);
180 LuminanceReduceRasterMode mode = final ? LUMINANCE_REDUCE_FRAGMENT_FINAL : (i == 0 ? LUMINANCE_REDUCE_FRAGMENT_FIRST : LUMINANCE_REDUCE_FRAGMENT);
181 RID shader = luminance_reduce_raster.shader.version_get_shader(luminance_reduce_raster.shader_version, mode);
182
183 RID framebuffer = FramebufferCacheRD::get_singleton()->get_cache(p_luminance_buffers->reduce[i]);
184
185 RD::Uniform u_source_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, i == 0 ? p_source_texture : p_luminance_buffers->reduce[i - 1] }));
186
187 RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
188 RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, luminance_reduce_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
189 RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0);
190 if (final) {
191 RD::Uniform u_current_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_luminance_buffers->current }));
192 RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_current_texture), 1);
193 }
194
195 RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(LuminanceReduceRasterPushConstant));
196
197 RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
198 RD::get_singleton()->draw_list_end();
199 }
200 } else {
201 LuminanceReducePushConstant push_constant;
202 memset(&push_constant, 0, sizeof(LuminanceReducePushConstant));
203
204 push_constant.source_size[0] = p_source_size.x;
205 push_constant.source_size[1] = p_source_size.y;
206 push_constant.max_luminance = p_max_luminance;
207 push_constant.min_luminance = p_min_luminance;
208 push_constant.exposure_adjust = p_adjust;
209
210 RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
211
212 for (int i = 0; i < p_luminance_buffers->reduce.size(); i++) {
213 RID shader;
214
215 if (i == 0) {
216 shader = luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, LUMINANCE_REDUCE_READ);
217 RD::Uniform u_source_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_texture }));
218
219 RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_READ]);
220 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0);
221 } else {
222 RD::get_singleton()->compute_list_add_barrier(compute_list); //needs barrier, wait until previous is done
223
224 if (i == p_luminance_buffers->reduce.size() - 1 && !p_set) {
225 shader = luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, LUMINANCE_REDUCE_WRITE);
226 RD::Uniform u_current_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_luminance_buffers->current }));
227
228 RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_WRITE]);
229 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_current_texture), 2);
230 } else {
231 shader = luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, LUMINANCE_REDUCE);
232 RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE]);
233 }
234
235 RD::Uniform u_source_texture(RD::UNIFORM_TYPE_IMAGE, 0, p_luminance_buffers->reduce[i - 1]);
236 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0);
237 }
238
239 RD::Uniform u_reduce_texture(RD::UNIFORM_TYPE_IMAGE, 0, p_luminance_buffers->reduce[i]);
240 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_reduce_texture), 1);
241
242 RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(LuminanceReducePushConstant));
243
244 RD::get_singleton()->compute_list_dispatch_threads(compute_list, push_constant.source_size[0], push_constant.source_size[1], 1);
245
246 push_constant.source_size[0] = MAX(push_constant.source_size[0] / 8, 1);
247 push_constant.source_size[1] = MAX(push_constant.source_size[1] / 8, 1);
248 }
249
250 RD::get_singleton()->compute_list_end();
251 }
252
253 SWAP(p_luminance_buffers->current, p_luminance_buffers->reduce.write[p_luminance_buffers->reduce.size() - 1]);
254}
255