1 // license:BSD-3-Clause
2 // copyright-holders:Ryan Holtz
3 //============================================================
4 //
5 // chainentry.cpp - BGFX shader post-processing node
6 //
7 // Represents a single entry in a list of post-processing
8 // passes to be applied to a screen quad or buffer.
9 //
10 //============================================================
11
12 #include "emu.h"
13
14 #include <bgfx/bgfx.h>
15 #include <bx/math.h>
16 #include <cmath>
17
18 #include "chainmanager.h"
19 #include "chainentry.h"
20
21 #include "effect.h"
22 #include "clear.h"
23 #include "texture.h"
24 #include "target.h"
25 #include "entryuniform.h"
26 #include "texturemanager.h"
27 #include "vertex.h"
28 #include "suppressor.h"
29
30 #include "render.h"
31
32
bgfx_chain_entry(std::string name,bgfx_effect * effect,clear_state * clear,std::vector<bgfx_suppressor * > suppressors,std::vector<bgfx_input_pair * > inputs,std::vector<bgfx_entry_uniform * > uniforms,target_manager & targets,std::string output,bool apply_tint)33 bgfx_chain_entry::bgfx_chain_entry(std::string name, bgfx_effect* effect, clear_state* clear, std::vector<bgfx_suppressor*> suppressors, std::vector<bgfx_input_pair*> inputs, std::vector<bgfx_entry_uniform*> uniforms, target_manager& targets, std::string output, bool apply_tint)
34 : m_name(name)
35 , m_effect(effect)
36 , m_clear(clear)
37 , m_suppressors(suppressors)
38 , m_inputs(inputs)
39 , m_uniforms(uniforms)
40 , m_targets(targets)
41 , m_output(output)
42 , m_apply_tint(apply_tint)
43 {
44 }
45
~bgfx_chain_entry()46 bgfx_chain_entry::~bgfx_chain_entry()
47 {
48 for (bgfx_input_pair* input : m_inputs)
49 {
50 delete input;
51 }
52 m_inputs.clear();
53 for (bgfx_entry_uniform* uniform : m_uniforms)
54 {
55 delete uniform;
56 }
57 m_uniforms.clear();
58 delete m_clear;
59 }
60
submit(int view,chain_manager::screen_prim & prim,texture_manager & textures,uint16_t screen_count,uint16_t screen_width,uint16_t screen_height,float screen_scale_x,float screen_scale_y,float screen_offset_x,float screen_offset_y,uint32_t rotation_type,bool swap_xy,uint64_t blend,int32_t screen)61 void bgfx_chain_entry::submit(int view, chain_manager::screen_prim &prim, texture_manager& textures, uint16_t screen_count, uint16_t screen_width, uint16_t screen_height, float screen_scale_x, float screen_scale_y, float screen_offset_x, float screen_offset_y, uint32_t rotation_type, bool swap_xy, uint64_t blend, int32_t screen)
62 {
63 if (!setup_view(textures, view, screen_width, screen_height, screen))
64 {
65 return;
66 }
67
68 for (bgfx_input_pair* input : m_inputs)
69 {
70 input->bind(m_effect, screen);
71 }
72
73 uint32_t tint = 0xffffffff;
74 if (m_apply_tint)
75 {
76 const auto a = (uint8_t)std::round(prim.m_prim->color.a * 255);
77 const auto r = (uint8_t)std::round(prim.m_prim->color.r * 255);
78 const auto g = (uint8_t)std::round(prim.m_prim->color.g * 255);
79 const auto b = (uint8_t)std::round(prim.m_prim->color.b * 255);
80 tint = (a << 24) | (b << 16) | (g << 8) | r;
81 }
82
83 bgfx::TransientVertexBuffer buffer;
84 put_screen_buffer(prim.m_screen_width, prim.m_screen_height, tint, &buffer);
85 bgfx::setVertexBuffer(0, &buffer);
86
87 setup_auto_uniforms(prim, textures, screen_count, screen_width, screen_height, screen_scale_x, screen_scale_y, screen_offset_x, screen_offset_y, rotation_type, swap_xy, screen);
88
89 for (bgfx_entry_uniform* uniform : m_uniforms)
90 {
91 if (uniform->name() != "s_tex")
92 {
93 uniform->bind();
94 }
95 }
96
97 m_effect->submit(view, blend);
98
99 if (m_targets.target(screen, m_output) != nullptr)
100 {
101 m_targets.target(screen, m_output)->page_flip();
102 }
103 }
104
setup_screensize_uniforms(texture_manager & textures,uint16_t screen_width,uint16_t screen_height,int32_t screen)105 void bgfx_chain_entry::setup_screensize_uniforms(texture_manager& textures, uint16_t screen_width, uint16_t screen_height, int32_t screen)
106 {
107 float width = screen_width;
108 float height = screen_height;
109 if (m_inputs.size() > 0)
110 {
111 std::string name = m_inputs[0]->texture() + std::to_string(screen);
112 width = float(textures.provider(name)->width());
113 height = float(textures.provider(name)->height());
114 }
115
116 bgfx_uniform* screen_dims = m_effect->uniform("u_screen_dims");
117 if (screen_dims != nullptr)
118 {
119 float values[2] = { width, height };
120 screen_dims->set(values, sizeof(float) * 2);
121 }
122
123 bgfx_uniform* inv_screen_dims = m_effect->uniform("u_inv_screen_dims");
124 if (inv_screen_dims != nullptr)
125 {
126 float values[2] = { 1.0f / width, 1.0f / height };
127 inv_screen_dims->set(values, sizeof(float) * 2);
128 }
129 }
130
setup_screenscale_uniforms(float screen_scale_x,float screen_scale_y)131 void bgfx_chain_entry::setup_screenscale_uniforms(float screen_scale_x, float screen_scale_y)
132 {
133 bgfx_uniform* screen_scale = m_effect->uniform("u_screen_scale");
134 if (screen_scale != nullptr)
135 {
136 float values[2] = { screen_scale_x, screen_scale_y };
137 screen_scale->set(values, sizeof(float) * 2);
138 }
139 }
140
setup_screenoffset_uniforms(float screen_offset_x,float screen_offset_y)141 void bgfx_chain_entry::setup_screenoffset_uniforms(float screen_offset_x, float screen_offset_y)
142 {
143 bgfx_uniform* screen_offset = m_effect->uniform("u_screen_offset");
144 if (screen_offset != nullptr)
145 {
146 float values[2] = { screen_offset_x, screen_offset_y };
147 screen_offset->set(values, sizeof(float) * 2);
148 }
149 }
150
setup_screencount_uniforms(uint16_t screen_count)151 void bgfx_chain_entry::setup_screencount_uniforms(uint16_t screen_count)
152 {
153 bgfx_uniform* u_screen_count = m_effect->uniform("u_screen_count");
154 if (u_screen_count != nullptr)
155 {
156 float values[1] = { float(screen_count) };
157 u_screen_count->set(values, sizeof(float));
158 }
159 }
160
setup_sourcesize_uniform(chain_manager::screen_prim & prim) const161 void bgfx_chain_entry::setup_sourcesize_uniform(chain_manager::screen_prim &prim) const
162 {
163 bgfx_uniform* source_dims = m_effect->uniform("u_source_dims");
164 if (source_dims != nullptr)
165 {
166 source_dims->set(&prim.m_tex_width, sizeof(float) * 2);
167 }
168 }
169
setup_targetsize_uniform(int32_t screen) const170 void bgfx_chain_entry::setup_targetsize_uniform(int32_t screen) const
171 {
172 bgfx_uniform* target_dims = m_effect->uniform("u_target_dims");
173 if (target_dims != nullptr)
174 {
175 bgfx_target* output = m_targets.target(screen, m_output);
176 if (output != nullptr)
177 {
178 float values[2] = { float(output->width()), float(output->height()) };
179 target_dims->set(values, sizeof(float) * 2);
180 }
181 }
182 }
183
setup_targetscale_uniform(int32_t screen) const184 void bgfx_chain_entry::setup_targetscale_uniform(int32_t screen) const
185 {
186 bgfx_uniform* target_scale = m_effect->uniform("u_target_scale");
187 if (target_scale != nullptr)
188 {
189 bgfx_target* output = m_targets.target(screen, m_output);
190 if (output != nullptr)
191 {
192 float values[2] = { float(output->scale()), float(output->scale()) };
193 target_scale->set(values, sizeof(float) * 2);
194 }
195 }
196 }
197
setup_rotationtype_uniform(uint32_t rotation_type) const198 void bgfx_chain_entry::setup_rotationtype_uniform(uint32_t rotation_type) const
199 {
200 bgfx_uniform* rotation_type_uniform = m_effect->uniform("u_rotation_type");
201 if (rotation_type_uniform != nullptr)
202 {
203 float values[1] = { float(rotation_type) };
204 rotation_type_uniform->set(values, sizeof(float));
205 }
206 }
207
setup_swapxy_uniform(bool swap_xy) const208 void bgfx_chain_entry::setup_swapxy_uniform(bool swap_xy) const
209 {
210 bgfx_uniform* swap_xy_uniform = m_effect->uniform("u_swap_xy");
211 if (swap_xy_uniform != nullptr)
212 {
213 float values[1] = { swap_xy ? 1.0f : 0.0f };
214 swap_xy_uniform->set(values, sizeof(float));
215 }
216 }
217
setup_quaddims_uniform(chain_manager::screen_prim & prim) const218 void bgfx_chain_entry::setup_quaddims_uniform(chain_manager::screen_prim &prim) const
219 {
220 bgfx_uniform* quad_dims_uniform = m_effect->uniform("u_quad_dims");
221 if (quad_dims_uniform != nullptr)
222 {
223 float values[2] = { float(prim.m_quad_width), float(prim.m_quad_height) };
224 quad_dims_uniform->set(values, sizeof(float) * 2);
225 }
226 }
227
setup_screenindex_uniform(int32_t screen) const228 void bgfx_chain_entry::setup_screenindex_uniform(int32_t screen) const
229 {
230 bgfx_uniform* screen_index = m_effect->uniform("u_screen_index");
231 if (screen_index != nullptr)
232 {
233 float values[1] = { float(screen) };
234 screen_index->set(values, sizeof(float));
235 }
236 }
237
setup_auto_uniforms(chain_manager::screen_prim & prim,texture_manager & textures,uint16_t screen_count,uint16_t screen_width,uint16_t screen_height,float screen_scale_x,float screen_scale_y,float screen_offset_x,float screen_offset_y,uint32_t rotation_type,bool swap_xy,int32_t screen)238 void bgfx_chain_entry::setup_auto_uniforms(chain_manager::screen_prim &prim, texture_manager& textures, uint16_t screen_count, uint16_t screen_width, uint16_t screen_height, float screen_scale_x, float screen_scale_y, float screen_offset_x, float screen_offset_y, uint32_t rotation_type, bool swap_xy, int32_t screen)
239 {
240 setup_screensize_uniforms(textures, screen_width, screen_height, screen);
241 setup_screenscale_uniforms(screen_scale_x, screen_scale_y);
242 setup_screenoffset_uniforms(screen_offset_x, screen_offset_y);
243 setup_screencount_uniforms(screen_count);
244 setup_sourcesize_uniform(prim);
245 setup_targetsize_uniform(screen);
246 setup_targetscale_uniform(screen);
247 setup_rotationtype_uniform(rotation_type);
248 setup_swapxy_uniform(swap_xy);
249 setup_quaddims_uniform(prim);
250 setup_screenindex_uniform(screen);
251 }
252
setup_view(texture_manager & textures,int view,uint16_t screen_width,uint16_t screen_height,int32_t screen) const253 bool bgfx_chain_entry::setup_view(texture_manager &textures, int view, uint16_t screen_width, uint16_t screen_height, int32_t screen) const
254 {
255 bgfx::FrameBufferHandle handle = BGFX_INVALID_HANDLE;
256 uint16_t width = screen_width;
257 uint16_t height = screen_height;
258 if (m_targets.target(screen, m_output) != nullptr)
259 {
260 bgfx_target* output = m_targets.target(screen, m_output);
261 if (output->width() == 0)
262 {
263 return false;
264 }
265 handle = output->target();
266 width = output->width();
267 height = output->height();
268 }
269
270 bgfx::setViewFrameBuffer(view, handle);
271 bgfx::setViewRect(view, 0, 0, width, height);
272
273 const bgfx::Caps* caps = bgfx::getCaps();
274
275 std::string name = m_inputs[0]->texture() + std::to_string(screen);
276 const float right_ratio = (float)textures.provider(name)->width() / textures.provider(name)->rowpixels();
277
278 float projMat[16];
279 bx::mtxOrtho(projMat, 0.0f, right_ratio, 1.0f, 0.0f, 0.0f, 100.0f, 0.0f, caps->homogeneousDepth);
280 bgfx::setViewTransform(view, nullptr, projMat);
281
282 m_clear->bind(view);
283 return true;
284 }
285
put_screen_buffer(uint16_t screen_width,uint16_t screen_height,uint32_t screen_tint,bgfx::TransientVertexBuffer * buffer) const286 void bgfx_chain_entry::put_screen_buffer(uint16_t screen_width, uint16_t screen_height, uint32_t screen_tint, bgfx::TransientVertexBuffer* buffer) const
287 {
288 if (6 == bgfx::getAvailTransientVertexBuffer(6, ScreenVertex::ms_decl))
289 {
290 bgfx::allocTransientVertexBuffer(buffer, 6, ScreenVertex::ms_decl);
291 }
292 else
293 {
294 return;
295 }
296
297 auto* vertex = reinterpret_cast<ScreenVertex*>(buffer->data);
298
299 float x[4] = { 0, 1, 0, 1 };
300 float y[4] = { 0, 0, 1, 1 };
301 float u[4] = { 0, 1, 0, 1 };
302 float v[4] = { 0, 0, 1, 1 };
303
304 bgfx::RendererType::Enum renderer_type = bgfx::getRendererType();
305 if (renderer_type == bgfx::RendererType::OpenGL || renderer_type == bgfx::RendererType::OpenGLES)
306 {
307 v[0] = v[1] = 1;
308 v[2] = v[3] = 0;
309 }
310 else if (renderer_type == bgfx::RendererType::Direct3D9)
311 {
312 for (int i = 0; i < 4; i++)
313 {
314 u[i] += 0.5f / screen_width;
315 v[i] += 0.5f / screen_height;
316 }
317 }
318
319 vertex[0].m_x = x[0];
320 vertex[0].m_y = y[0];
321 vertex[0].m_z = 0;
322 vertex[0].m_rgba = screen_tint;
323 vertex[0].m_u = u[0];
324 vertex[0].m_v = v[0];
325
326 vertex[1].m_x = x[1];
327 vertex[1].m_y = y[1];
328 vertex[1].m_z = 0;
329 vertex[1].m_rgba = screen_tint;
330 vertex[1].m_u = u[1];
331 vertex[1].m_v = v[1];
332
333 vertex[2].m_x = x[3];
334 vertex[2].m_y = y[3];
335 vertex[2].m_z = 0;
336 vertex[2].m_rgba = screen_tint;
337 vertex[2].m_u = u[3];
338 vertex[2].m_v = v[3];
339
340 vertex[3].m_x = x[3];
341 vertex[3].m_y = y[3];
342 vertex[3].m_z = 0;
343 vertex[3].m_rgba = screen_tint;
344 vertex[3].m_u = u[3];
345 vertex[3].m_v = v[3];
346
347 vertex[4].m_x = x[2];
348 vertex[4].m_y = y[2];
349 vertex[4].m_z = 0;
350 vertex[4].m_rgba = screen_tint;
351 vertex[4].m_u = u[2];
352 vertex[4].m_v = v[2];
353
354 vertex[5].m_x = x[0];
355 vertex[5].m_y = y[0];
356 vertex[5].m_z = 0;
357 vertex[5].m_rgba = screen_tint;
358 vertex[5].m_u = u[0];
359 vertex[5].m_v = v[0];
360 }
361
skip()362 bool bgfx_chain_entry::skip()
363 {
364 if (m_suppressors.size() == 0)
365 {
366 return false;
367 }
368
369 // Group all AND/OR'd results together and OR them together (hack for now)
370 // TODO: Make this a bit more logical
371
372 bool or_suppress = false;
373 int and_count = 0;
374 int and_suppressed = 0;
375 for (bgfx_suppressor* suppressor : m_suppressors)
376 {
377 if (suppressor->combine() == bgfx_suppressor::combine_mode::COMBINE_AND)
378 {
379 and_count++;
380 if (suppressor->suppress())
381 {
382 and_suppressed++;
383 }
384 }
385 else if (suppressor->combine() == bgfx_suppressor::combine_mode::COMBINE_OR)
386 {
387 or_suppress |= suppressor->suppress();
388 }
389 }
390
391 return (and_count != 0 && and_suppressed == and_count) || or_suppress;
392 }
393