1/**************************************************************************/
2/* bokeh_dof.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 "bokeh_dof.h"
32#include "copy_effects.h"
33#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
34#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
35#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
36#include "servers/rendering/rendering_server_default.h"
37#include "servers/rendering/storage/camera_attributes_storage.h"
38
39using namespace RendererRD;
40
41BokehDOF::BokehDOF(bool p_prefer_raster_effects) {
42 prefer_raster_effects = p_prefer_raster_effects;
43
44 // Initialize bokeh
45 Vector<String> bokeh_modes;
46 bokeh_modes.push_back("\n#define MODE_GEN_BLUR_SIZE\n");
47 bokeh_modes.push_back("\n#define MODE_BOKEH_BOX\n#define OUTPUT_WEIGHT\n");
48 bokeh_modes.push_back("\n#define MODE_BOKEH_BOX\n");
49 bokeh_modes.push_back("\n#define MODE_BOKEH_HEXAGONAL\n#define OUTPUT_WEIGHT\n");
50 bokeh_modes.push_back("\n#define MODE_BOKEH_HEXAGONAL\n");
51 bokeh_modes.push_back("\n#define MODE_BOKEH_CIRCULAR\n#define OUTPUT_WEIGHT\n");
52 bokeh_modes.push_back("\n#define MODE_COMPOSITE_BOKEH\n");
53 if (prefer_raster_effects) {
54 bokeh.raster_shader.initialize(bokeh_modes);
55
56 bokeh.shader_version = bokeh.raster_shader.version_create();
57
58 const int att_count[BOKEH_MAX] = { 1, 2, 1, 2, 1, 2, 1 };
59 for (int i = 0; i < BOKEH_MAX; i++) {
60 RD::PipelineColorBlendState blend_state = (i == BOKEH_COMPOSITE) ? RD::PipelineColorBlendState::create_blend(att_count[i]) : RD::PipelineColorBlendState::create_disabled(att_count[i]);
61 bokeh.raster_pipelines[i].setup(bokeh.raster_shader.version_get_shader(bokeh.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, 0);
62 }
63 } else {
64 bokeh.compute_shader.initialize(bokeh_modes);
65 bokeh.compute_shader.set_variant_enabled(BOKEH_GEN_BOKEH_BOX_NOWEIGHT, false);
66 bokeh.compute_shader.set_variant_enabled(BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT, false);
67 bokeh.shader_version = bokeh.compute_shader.version_create();
68
69 for (int i = 0; i < BOKEH_MAX; i++) {
70 if (bokeh.compute_shader.is_variant_enabled(i)) {
71 bokeh.compute_pipelines[i] = RD::get_singleton()->compute_pipeline_create(bokeh.compute_shader.version_get_shader(bokeh.shader_version, i));
72 }
73 }
74
75 for (int i = 0; i < BOKEH_MAX; i++) {
76 bokeh.raster_pipelines[i].clear();
77 }
78 }
79}
80
81BokehDOF::~BokehDOF() {
82 if (prefer_raster_effects) {
83 bokeh.raster_shader.version_free(bokeh.shader_version);
84 } else {
85 bokeh.compute_shader.version_free(bokeh.shader_version);
86 }
87}
88
89void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, RID p_camera_attributes, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) {
90 ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute version of bokeh depth of field with the mobile renderer.");
91
92 UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
93 ERR_FAIL_NULL(uniform_set_cache);
94 MaterialStorage *material_storage = MaterialStorage::get_singleton();
95 ERR_FAIL_NULL(material_storage);
96
97 bool dof_far = RSG::camera_attributes->camera_attributes_get_dof_far_enabled(p_camera_attributes);
98 float dof_far_begin = RSG::camera_attributes->camera_attributes_get_dof_far_distance(p_camera_attributes);
99 float dof_far_size = RSG::camera_attributes->camera_attributes_get_dof_far_transition(p_camera_attributes);
100 bool dof_near = RSG::camera_attributes->camera_attributes_get_dof_near_enabled(p_camera_attributes);
101 float dof_near_begin = RSG::camera_attributes->camera_attributes_get_dof_near_distance(p_camera_attributes);
102 float dof_near_size = RSG::camera_attributes->camera_attributes_get_dof_near_transition(p_camera_attributes);
103 float bokeh_size = RSG::camera_attributes->camera_attributes_get_dof_blur_amount(p_camera_attributes) * 64; // Base 64 pixel radius.
104
105 bool use_jitter = RSG::camera_attributes->camera_attributes_get_dof_blur_use_jitter();
106 RS::DOFBokehShape bokeh_shape = RSG::camera_attributes->camera_attributes_get_dof_blur_bokeh_shape();
107 RS::DOFBlurQuality blur_quality = RSG::camera_attributes->camera_attributes_get_dof_blur_quality();
108
109 // setup our push constant
110 memset(&bokeh.push_constant, 0, sizeof(BokehPushConstant));
111 bokeh.push_constant.blur_far_active = dof_far;
112 bokeh.push_constant.blur_far_begin = dof_far_begin;
113 bokeh.push_constant.blur_far_end = dof_far_begin + dof_far_size; // Only used with non-physically-based.
114 bokeh.push_constant.use_physical_far = dof_far_size < 0.0;
115 bokeh.push_constant.blur_size_far = bokeh_size; // Only used with physically-based.
116
117 bokeh.push_constant.blur_near_active = dof_near;
118 bokeh.push_constant.blur_near_begin = dof_near_begin;
119 bokeh.push_constant.blur_near_end = dof_near_begin - dof_near_size; // Only used with non-physically-based.
120 bokeh.push_constant.use_physical_near = dof_near_size < 0.0;
121 bokeh.push_constant.blur_size_near = bokeh_size; // Only used with physically-based.
122
123 bokeh.push_constant.use_jitter = use_jitter;
124 bokeh.push_constant.jitter_seed = Math::randf() * 1000.0;
125
126 bokeh.push_constant.z_near = p_cam_znear;
127 bokeh.push_constant.z_far = p_cam_zfar;
128 bokeh.push_constant.orthogonal = p_cam_orthogonal;
129 bokeh.push_constant.blur_size = (dof_near_size < 0.0 && dof_far_size < 0.0) ? 32 : bokeh_size; // Cap with physically-based to keep performance reasonable.
130
131 bokeh.push_constant.second_pass = false;
132 bokeh.push_constant.half_size = false;
133
134 bokeh.push_constant.blur_scale = 0.5;
135
136 // setup our uniforms
137 RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
138
139 RD::Uniform u_base_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.base_texture }));
140 RD::Uniform u_depth_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.depth_texture }));
141 RD::Uniform u_secondary_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.secondary_texture }));
142 RD::Uniform u_half_texture0(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.half_texture[0] }));
143 RD::Uniform u_half_texture1(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.half_texture[1] }));
144
145 RD::Uniform u_base_image(RD::UNIFORM_TYPE_IMAGE, 0, p_buffers.base_texture);
146 RD::Uniform u_secondary_image(RD::UNIFORM_TYPE_IMAGE, 0, p_buffers.secondary_texture);
147 RD::Uniform u_half_image0(RD::UNIFORM_TYPE_IMAGE, 0, p_buffers.half_texture[0]);
148 RD::Uniform u_half_image1(RD::UNIFORM_TYPE_IMAGE, 0, p_buffers.half_texture[1]);
149
150 RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
151
152 /* FIRST PASS */
153 // The alpha channel of the source color texture is filled with the expected circle size
154 // If used for DOF far, the size is positive, if used for near, its negative.
155
156 RID shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, BOKEH_GEN_BLUR_SIZE);
157 ERR_FAIL_COND(shader.is_null());
158
159 RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_GEN_BLUR_SIZE]);
160
161 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_base_image), 0);
162 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_depth_texture), 1);
163
164 bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
165 bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
166
167 RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
168
169 RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1);
170 RD::get_singleton()->compute_list_add_barrier(compute_list);
171
172 if (bokeh_shape == RS::DOF_BOKEH_BOX || bokeh_shape == RS::DOF_BOKEH_HEXAGON) {
173 //second pass
174 BokehMode mode = bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL;
175 shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, mode);
176 ERR_FAIL_COND(shader.is_null());
177
178 RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[mode]);
179
180 static const int quality_samples[4] = { 6, 12, 12, 24 };
181
182 bokeh.push_constant.steps = quality_samples[blur_quality];
183
184 if (blur_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || blur_quality == RS::DOF_BLUR_QUALITY_LOW) {
185 //box and hexagon are more or less the same, and they can work in either half (very low and low quality) or full (medium and high quality_ sizes)
186
187 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_half_image0), 0);
188 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_base_texture), 1);
189
190 bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
191 bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
192 bokeh.push_constant.half_size = true;
193 bokeh.push_constant.blur_size *= 0.5;
194
195 } else {
196 //medium and high quality use full size
197 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_secondary_image), 0);
198 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_base_texture), 1);
199 }
200
201 RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
202
203 RD::get_singleton()->compute_list_dispatch_threads(compute_list, bokeh.push_constant.size[0], bokeh.push_constant.size[1], 1);
204 RD::get_singleton()->compute_list_add_barrier(compute_list);
205
206 //third pass
207 bokeh.push_constant.second_pass = true;
208
209 if (blur_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || blur_quality == RS::DOF_BLUR_QUALITY_LOW) {
210 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_half_image1), 0);
211 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_half_texture0), 1);
212 } else {
213 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_base_image), 0);
214 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_secondary_texture), 1);
215 }
216
217 RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
218
219 RD::get_singleton()->compute_list_dispatch_threads(compute_list, bokeh.push_constant.size[0], bokeh.push_constant.size[1], 1);
220 RD::get_singleton()->compute_list_add_barrier(compute_list);
221
222 if (blur_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || blur_quality == RS::DOF_BLUR_QUALITY_LOW) {
223 //forth pass, upscale for low quality
224
225 shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, BOKEH_COMPOSITE);
226 ERR_FAIL_COND(shader.is_null());
227
228 RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_COMPOSITE]);
229
230 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_base_image), 0);
231 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_half_texture1), 1);
232
233 bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
234 bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
235 bokeh.push_constant.half_size = false;
236 bokeh.push_constant.second_pass = false;
237
238 RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
239
240 RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1);
241 }
242 } else {
243 //circle
244
245 shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, BOKEH_GEN_BOKEH_CIRCULAR);
246 ERR_FAIL_COND(shader.is_null());
247
248 //second pass
249 RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_GEN_BOKEH_CIRCULAR]);
250
251 static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 };
252
253 bokeh.push_constant.steps = 0;
254 bokeh.push_constant.blur_scale = quality_scale[blur_quality];
255
256 //circle always runs in half size, otherwise too expensive
257
258 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_half_image0), 0);
259 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_base_texture), 1);
260
261 bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
262 bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
263 bokeh.push_constant.half_size = true;
264
265 RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
266
267 RD::get_singleton()->compute_list_dispatch_threads(compute_list, bokeh.push_constant.size[0], bokeh.push_constant.size[1], 1);
268 RD::get_singleton()->compute_list_add_barrier(compute_list);
269
270 //circle is just one pass, then upscale
271
272 // upscale
273
274 shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, BOKEH_COMPOSITE);
275 ERR_FAIL_COND(shader.is_null());
276
277 RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_COMPOSITE]);
278
279 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_base_image), 0);
280 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_half_texture0), 1);
281
282 bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
283 bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
284 bokeh.push_constant.half_size = false;
285 bokeh.push_constant.second_pass = false;
286
287 RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
288
289 RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1);
290 }
291
292 RD::get_singleton()->compute_list_end();
293}
294
295void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attributes, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) {
296 ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't blur-based depth of field with the clustered renderer.");
297
298 UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
299 ERR_FAIL_NULL(uniform_set_cache);
300 MaterialStorage *material_storage = MaterialStorage::get_singleton();
301 ERR_FAIL_NULL(material_storage);
302
303 bool dof_far = RSG::camera_attributes->camera_attributes_get_dof_far_enabled(p_camera_attributes);
304 float dof_far_begin = RSG::camera_attributes->camera_attributes_get_dof_far_distance(p_camera_attributes);
305 float dof_far_size = RSG::camera_attributes->camera_attributes_get_dof_far_transition(p_camera_attributes);
306 bool dof_near = RSG::camera_attributes->camera_attributes_get_dof_near_enabled(p_camera_attributes);
307 float dof_near_begin = RSG::camera_attributes->camera_attributes_get_dof_near_distance(p_camera_attributes);
308 float dof_near_size = RSG::camera_attributes->camera_attributes_get_dof_near_transition(p_camera_attributes);
309 float bokeh_size = RSG::camera_attributes->camera_attributes_get_dof_blur_amount(p_camera_attributes) * 64; // Base 64 pixel radius.
310
311 RS::DOFBokehShape bokeh_shape = RSG::camera_attributes->camera_attributes_get_dof_blur_bokeh_shape();
312 RS::DOFBlurQuality blur_quality = RSG::camera_attributes->camera_attributes_get_dof_blur_quality();
313
314 // setup our base push constant
315 memset(&bokeh.push_constant, 0, sizeof(BokehPushConstant));
316
317 bokeh.push_constant.orthogonal = p_cam_orthogonal;
318 bokeh.push_constant.size[0] = p_buffers.base_texture_size.width;
319 bokeh.push_constant.size[1] = p_buffers.base_texture_size.height;
320 bokeh.push_constant.z_far = p_cam_zfar;
321 bokeh.push_constant.z_near = p_cam_znear;
322
323 bokeh.push_constant.second_pass = false;
324 bokeh.push_constant.half_size = false;
325 bokeh.push_constant.blur_size = bokeh_size;
326
327 // setup our uniforms
328 RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
329
330 RD::Uniform u_base_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.base_texture }));
331 RD::Uniform u_depth_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.depth_texture }));
332 RD::Uniform u_secondary_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.secondary_texture }));
333 RD::Uniform u_half_texture0(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.half_texture[0] }));
334 RD::Uniform u_half_texture1(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.half_texture[1] }));
335 RD::Uniform u_weight_texture0(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.weight_texture[0] }));
336 RD::Uniform u_weight_texture1(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.weight_texture[1] }));
337 RD::Uniform u_weight_texture2(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.weight_texture[2] }));
338 RD::Uniform u_weight_texture3(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.weight_texture[3] }));
339
340 if (dof_far || dof_near) {
341 if (dof_far) {
342 bokeh.push_constant.blur_far_active = true;
343 bokeh.push_constant.blur_far_begin = dof_far_begin;
344 bokeh.push_constant.blur_far_end = dof_far_begin + dof_far_size;
345 }
346
347 if (dof_near) {
348 bokeh.push_constant.blur_near_active = true;
349 bokeh.push_constant.blur_near_begin = dof_near_begin;
350 bokeh.push_constant.blur_near_end = dof_near_begin - dof_near_size;
351 }
352
353 {
354 // generate our depth data
355 RID shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, BOKEH_GEN_BLUR_SIZE);
356 ERR_FAIL_COND(shader.is_null());
357
358 RID framebuffer = p_buffers.base_weight_fb;
359 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);
360 RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[BOKEH_GEN_BLUR_SIZE].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
361 RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_depth_texture), 0);
362
363 RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
364
365 RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
366 RD::get_singleton()->draw_list_end();
367 }
368
369 if (bokeh_shape == RS::DOF_BOKEH_BOX || bokeh_shape == RS::DOF_BOKEH_HEXAGON) {
370 // double pass approach
371 BokehMode mode = bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL;
372
373 RID shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode);
374 ERR_FAIL_COND(shader.is_null());
375
376 if (blur_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || blur_quality == RS::DOF_BLUR_QUALITY_LOW) {
377 //box and hexagon are more or less the same, and they can work in either half (very low and low quality) or full (medium and high quality_ sizes)
378 bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
379 bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
380 bokeh.push_constant.half_size = true;
381 bokeh.push_constant.blur_size *= 0.5;
382 }
383
384 static const int quality_samples[4] = { 6, 12, 12, 24 };
385 bokeh.push_constant.blur_scale = 0.5;
386 bokeh.push_constant.steps = quality_samples[blur_quality];
387
388 RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb;
389
390 // Pass 1
391 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);
392 RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
393 RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_base_texture), 0);
394 RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture0), 1);
395
396 RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
397
398 RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
399 RD::get_singleton()->draw_list_end();
400
401 // Pass 2
402 if (!bokeh.push_constant.half_size) {
403 // do not output weight, we're writing back into our base buffer
404 mode = bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX_NOWEIGHT : BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT;
405
406 shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode);
407 ERR_FAIL_COND(shader.is_null());
408 }
409 bokeh.push_constant.second_pass = true;
410
411 framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[1] : p_buffers.base_fb;
412 RD::Uniform texture = bokeh.push_constant.half_size ? u_half_texture0 : u_secondary_texture;
413 RD::Uniform weight = bokeh.push_constant.half_size ? u_weight_texture2 : u_weight_texture1;
414
415 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);
416 RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
417 RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, texture), 0);
418 RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, weight), 1);
419
420 RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
421
422 RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
423 RD::get_singleton()->draw_list_end();
424
425 if (bokeh.push_constant.half_size) {
426 // Compose pass
427 mode = BOKEH_COMPOSITE;
428 shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode);
429 ERR_FAIL_COND(shader.is_null());
430
431 framebuffer = p_buffers.base_fb;
432
433 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);
434 RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
435 RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture1), 0);
436 RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture3), 1);
437 RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_weight_texture0), 2);
438
439 RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
440
441 RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
442 RD::get_singleton()->draw_list_end();
443 }
444
445 } else {
446 // circular is a single pass approach
447 BokehMode mode = BOKEH_GEN_BOKEH_CIRCULAR;
448
449 RID shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode);
450 ERR_FAIL_COND(shader.is_null());
451
452 {
453 // circle always runs in half size, otherwise too expensive (though the code below does support making this optional)
454 bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
455 bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
456 bokeh.push_constant.half_size = true;
457 // bokeh.push_constant.blur_size *= 0.5;
458 }
459
460 static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 };
461 bokeh.push_constant.blur_scale = quality_scale[blur_quality];
462 bokeh.push_constant.steps = 0.0;
463
464 RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb;
465
466 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);
467 RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
468 RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_base_texture), 0);
469 RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture0), 1);
470
471 RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
472
473 RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
474 RD::get_singleton()->draw_list_end();
475
476 if (bokeh.push_constant.half_size) {
477 // Compose
478 mode = BOKEH_COMPOSITE;
479 shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode);
480 ERR_FAIL_COND(shader.is_null());
481
482 framebuffer = p_buffers.base_fb;
483
484 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);
485 RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
486 RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture0), 0);
487 RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture2), 1);
488 RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_weight_texture0), 2);
489
490 RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
491
492 RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
493 RD::get_singleton()->draw_list_end();
494 } else {
495 CopyEffects::get_singleton()->copy_raster(p_buffers.secondary_texture, p_buffers.base_fb);
496 }
497 }
498 }
499}
500