Flutter Engine
The Flutter Engine
render_pass_gles.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <cstdint>
8
9#include "GLES3/gl3.h"
10#include "flutter/fml/trace_event.h"
11#include "fml/closure.h"
12#include "fml/logging.h"
20
21namespace impeller {
22
23RenderPassGLES::RenderPassGLES(std::shared_ptr<const Context> context,
24 const RenderTarget& target,
25 ReactorGLES::Ref reactor)
26 : RenderPass(std::move(context), target),
27 reactor_(std::move(reactor)),
28 is_valid_(reactor_ && reactor_->IsValid()) {}
29
30// |RenderPass|
31RenderPassGLES::~RenderPassGLES() = default;
32
33// |RenderPass|
34bool RenderPassGLES::IsValid() const {
35 return is_valid_;
36}
37
38// |RenderPass|
39void RenderPassGLES::OnSetLabel(std::string label) {
40 label_ = std::move(label);
41}
42
45 if (color->blending_enabled) {
46 gl.Enable(GL_BLEND);
47 gl.BlendFuncSeparate(
48 ToBlendFactor(color->src_color_blend_factor), // src color
49 ToBlendFactor(color->dst_color_blend_factor), // dst color
50 ToBlendFactor(color->src_alpha_blend_factor), // src alpha
51 ToBlendFactor(color->dst_alpha_blend_factor) // dst alpha
52 );
53 gl.BlendEquationSeparate(
54 ToBlendOperation(color->color_blend_op), // mode color
55 ToBlendOperation(color->alpha_blend_op) // mode alpha
56 );
57 } else {
58 gl.Disable(GL_BLEND);
59 }
60
61 {
62 const auto is_set = [](ColorWriteMask mask,
63 ColorWriteMask check) -> GLboolean {
64 return (mask & check) ? GL_TRUE : GL_FALSE;
65 };
66
67 gl.ColorMask(
68 is_set(color->write_mask, ColorWriteMaskBits::kRed), // red
69 is_set(color->write_mask, ColorWriteMaskBits::kGreen), // green
70 is_set(color->write_mask, ColorWriteMaskBits::kBlue), // blue
71 is_set(color->write_mask, ColorWriteMaskBits::kAlpha) // alpha
72 );
73 }
74}
75
76void ConfigureStencil(GLenum face,
77 const ProcTableGLES& gl,
78 const StencilAttachmentDescriptor& stencil,
79 uint32_t stencil_reference) {
80 gl.StencilOpSeparate(
81 face, // face
82 ToStencilOp(stencil.stencil_failure), // stencil fail
83 ToStencilOp(stencil.depth_failure), // depth fail
84 ToStencilOp(stencil.depth_stencil_pass) // depth stencil pass
85 );
86 gl.StencilFuncSeparate(face, // face
87 ToCompareFunction(stencil.stencil_compare), // func
88 stencil_reference, // ref
89 stencil.read_mask // mask
90 );
91 gl.StencilMaskSeparate(face, stencil.write_mask);
92}
93
95 const PipelineDescriptor& pipeline,
96 uint32_t stencil_reference) {
97 if (!pipeline.HasStencilAttachmentDescriptors()) {
98 gl.Disable(GL_STENCIL_TEST);
99 return;
100 }
101
102 gl.Enable(GL_STENCIL_TEST);
103 const auto& front = pipeline.GetFrontStencilAttachmentDescriptor();
104 const auto& back = pipeline.GetBackStencilAttachmentDescriptor();
105
106 if (front.has_value() && back.has_value() && front == back) {
107 ConfigureStencil(GL_FRONT_AND_BACK, gl, *front, stencil_reference);
108 return;
109 }
110 if (front.has_value()) {
111 ConfigureStencil(GL_FRONT, gl, *front, stencil_reference);
112 }
113 if (back.has_value()) {
114 ConfigureStencil(GL_BACK, gl, *back, stencil_reference);
115 }
116}
117
118//------------------------------------------------------------------------------
119/// @brief Encapsulates data that will be needed in the reactor for the
120/// encoding of commands for this render pass.
121///
124
126 uint32_t clear_stencil = 0u;
127 Scalar clear_depth = 1.0;
128
129 std::shared_ptr<Texture> color_attachment;
130 std::shared_ptr<Texture> depth_attachment;
131 std::shared_ptr<Texture> stencil_attachment;
132
133 bool clear_color_attachment = true;
134 bool clear_depth_attachment = true;
135 bool clear_stencil_attachment = true;
136
137 bool discard_color_attachment = true;
138 bool discard_depth_attachment = true;
139 bool discard_stencil_attachment = true;
140
141 std::string label;
142};
143
144[[nodiscard]] bool EncodeCommandsInReactor(
145 const RenderPassData& pass_data,
146 const std::shared_ptr<Allocator>& transients_allocator,
147 const ReactorGLES& reactor,
148 const std::vector<Command>& commands,
149 const std::shared_ptr<GPUTracerGLES>& tracer) {
150 TRACE_EVENT0("impeller", "RenderPassGLES::EncodeCommandsInReactor");
151
152 const auto& gl = reactor.GetProcTable();
153#ifdef IMPELLER_DEBUG
154 tracer->MarkFrameStart(gl);
155#endif // IMPELLER_DEBUG
156
157 fml::ScopedCleanupClosure pop_pass_debug_marker(
158 [&gl]() { gl.PopDebugGroup(); });
159 if (!pass_data.label.empty()) {
160 gl.PushDebugGroup(pass_data.label);
161 } else {
162 pop_pass_debug_marker.Release();
163 }
164
165 GLuint fbo = GL_NONE;
166 fml::ScopedCleanupClosure delete_fbo([&gl, &fbo]() {
167 if (fbo != GL_NONE) {
168 gl.BindFramebuffer(GL_FRAMEBUFFER, GL_NONE);
169 gl.DeleteFramebuffers(1u, &fbo);
170 }
171 });
172
173 TextureGLES& color_gles = TextureGLES::Cast(*pass_data.color_attachment);
174 const bool is_default_fbo = color_gles.IsWrapped();
175
176 if (is_default_fbo) {
177 if (color_gles.GetFBO().has_value()) {
178 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
179 gl.BindFramebuffer(GL_FRAMEBUFFER, *color_gles.GetFBO());
180 }
181 } else {
182 // Create and bind an offscreen FBO.
183 gl.GenFramebuffers(1u, &fbo);
184 gl.BindFramebuffer(GL_FRAMEBUFFER, fbo);
185
186 if (!color_gles.SetAsFramebufferAttachment(
187 GL_FRAMEBUFFER, TextureGLES::AttachmentType::kColor0)) {
188 return false;
189 }
190
191 if (auto depth = TextureGLES::Cast(pass_data.depth_attachment.get())) {
192 if (!depth->SetAsFramebufferAttachment(
193 GL_FRAMEBUFFER, TextureGLES::AttachmentType::kDepth)) {
194 return false;
195 }
196 }
197 if (auto stencil = TextureGLES::Cast(pass_data.stencil_attachment.get())) {
198 if (!stencil->SetAsFramebufferAttachment(
199 GL_FRAMEBUFFER, TextureGLES::AttachmentType::kStencil)) {
200 return false;
201 }
202 }
203
204 auto status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER);
205 if (status != GL_FRAMEBUFFER_COMPLETE) {
206 VALIDATION_LOG << "Could not create a complete frambuffer: "
207 << DebugToFramebufferError(status);
208 return false;
209 }
210 }
211
212 gl.ClearColor(pass_data.clear_color.red, // red
213 pass_data.clear_color.green, // green
214 pass_data.clear_color.blue, // blue
215 pass_data.clear_color.alpha // alpha
216 );
217 if (pass_data.depth_attachment) {
218 if (gl.DepthRangef.IsAvailable()) {
219 gl.ClearDepthf(pass_data.clear_depth);
220 } else {
221 gl.ClearDepth(pass_data.clear_depth);
222 }
223 }
224 if (pass_data.stencil_attachment) {
225 gl.ClearStencil(pass_data.clear_stencil);
226 }
227
228 GLenum clear_bits = 0u;
229 if (pass_data.clear_color_attachment) {
230 clear_bits |= GL_COLOR_BUFFER_BIT;
231 }
232 if (pass_data.clear_depth_attachment) {
233 clear_bits |= GL_DEPTH_BUFFER_BIT;
234 }
235 if (pass_data.clear_stencil_attachment) {
236 clear_bits |= GL_STENCIL_BUFFER_BIT;
237 }
238
239 gl.Disable(GL_SCISSOR_TEST);
240 gl.Disable(GL_DEPTH_TEST);
241 gl.Disable(GL_STENCIL_TEST);
242 gl.Disable(GL_CULL_FACE);
243 gl.Disable(GL_BLEND);
244 gl.ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
245 gl.DepthMask(GL_TRUE);
246 gl.StencilMaskSeparate(GL_FRONT, 0xFFFFFFFF);
247 gl.StencilMaskSeparate(GL_BACK, 0xFFFFFFFF);
248
249 gl.Clear(clear_bits);
250
251 for (const auto& command : commands) {
252 if (command.instance_count != 1u) {
253 VALIDATION_LOG << "GLES backend does not support instanced rendering.";
254 return false;
255 }
256
257 if (!command.pipeline) {
258 VALIDATION_LOG << "Command has no pipeline specified.";
259 return false;
260 }
261
262#ifdef IMPELLER_DEBUG
263 fml::ScopedCleanupClosure pop_cmd_debug_marker(
264 [&gl]() { gl.PopDebugGroup(); });
265 if (!command.label.empty()) {
266 gl.PushDebugGroup(command.label);
267 } else {
268 pop_cmd_debug_marker.Release();
269 }
270#endif // IMPELLER_DEBUG
271
272 const auto& pipeline = PipelineGLES::Cast(*command.pipeline);
273
274 const auto* color_attachment =
275 pipeline.GetDescriptor().GetLegacyCompatibleColorAttachment();
276 if (!color_attachment) {
278 << "Color attachment is too complicated for a legacy renderer.";
279 return false;
280 }
281
282 //--------------------------------------------------------------------------
283 /// Configure blending.
284 ///
285 ConfigureBlending(gl, color_attachment);
286
287 //--------------------------------------------------------------------------
288 /// Setup stencil.
289 ///
290 ConfigureStencil(gl, pipeline.GetDescriptor(), command.stencil_reference);
291
292 //--------------------------------------------------------------------------
293 /// Configure depth.
294 ///
295 if (auto depth =
296 pipeline.GetDescriptor().GetDepthStencilAttachmentDescriptor();
297 depth.has_value()) {
298 gl.Enable(GL_DEPTH_TEST);
299 gl.DepthFunc(ToCompareFunction(depth->depth_compare));
300 gl.DepthMask(depth->depth_write_enabled ? GL_TRUE : GL_FALSE);
301 } else {
302 gl.Disable(GL_DEPTH_TEST);
303 }
304
305 // Both the viewport and scissor are specified in framebuffer coordinates.
306 // Impeller's framebuffer coordinate system is top left origin, but OpenGL's
307 // is bottom left origin, so we convert the coordinates here.
308 auto target_size = pass_data.color_attachment->GetSize();
309
310 //--------------------------------------------------------------------------
311 /// Setup the viewport.
312 ///
313 const auto& viewport = command.viewport.value_or(pass_data.viewport);
314 gl.Viewport(viewport.rect.GetX(), // x
315 target_size.height - viewport.rect.GetY() -
316 viewport.rect.GetHeight(), // y
317 viewport.rect.GetWidth(), // width
318 viewport.rect.GetHeight() // height
319 );
320 if (pass_data.depth_attachment) {
321 if (gl.DepthRangef.IsAvailable()) {
322 gl.DepthRangef(viewport.depth_range.z_near, viewport.depth_range.z_far);
323 } else {
324 gl.DepthRange(viewport.depth_range.z_near, viewport.depth_range.z_far);
325 }
326 }
327
328 //--------------------------------------------------------------------------
329 /// Setup the scissor rect.
330 ///
331 if (command.scissor.has_value()) {
332 const auto& scissor = command.scissor.value();
333 gl.Enable(GL_SCISSOR_TEST);
334 gl.Scissor(
335 scissor.GetX(), // x
336 target_size.height - scissor.GetY() - scissor.GetHeight(), // y
337 scissor.GetWidth(), // width
338 scissor.GetHeight() // height
339 );
340 } else {
341 gl.Disable(GL_SCISSOR_TEST);
342 }
343
344 //--------------------------------------------------------------------------
345 /// Setup culling.
346 ///
347 switch (pipeline.GetDescriptor().GetCullMode()) {
348 case CullMode::kNone:
349 gl.Disable(GL_CULL_FACE);
350 break;
351 case CullMode::kFrontFace:
352 gl.Enable(GL_CULL_FACE);
353 gl.CullFace(GL_FRONT);
354 break;
355 case CullMode::kBackFace:
356 gl.Enable(GL_CULL_FACE);
357 gl.CullFace(GL_BACK);
358 break;
359 }
360 //--------------------------------------------------------------------------
361 /// Setup winding order.
362 ///
363 switch (pipeline.GetDescriptor().GetWindingOrder()) {
364 case WindingOrder::kClockwise:
365 gl.FrontFace(GL_CW);
366 break;
367 case WindingOrder::kCounterClockwise:
368 gl.FrontFace(GL_CCW);
369 break;
370 }
371
372 if (command.vertex_buffer.index_type == IndexType::kUnknown) {
373 return false;
374 }
375
376 auto vertex_desc_gles = pipeline.GetBufferBindings();
377
378 //--------------------------------------------------------------------------
379 /// Bind vertex and index buffers.
380 ///
381 auto& vertex_buffer_view = command.vertex_buffer.vertex_buffer;
382
383 if (!vertex_buffer_view) {
384 return false;
385 }
386
387 auto vertex_buffer = vertex_buffer_view.buffer;
388
389 if (!vertex_buffer) {
390 return false;
391 }
392
393 const auto& vertex_buffer_gles = DeviceBufferGLES::Cast(*vertex_buffer);
394 if (!vertex_buffer_gles.BindAndUploadDataIfNecessary(
395 DeviceBufferGLES::BindingType::kArrayBuffer)) {
396 return false;
397 }
398
399 //--------------------------------------------------------------------------
400 /// Bind the pipeline program.
401 ///
402 if (!pipeline.BindProgram()) {
403 return false;
404 }
405
406 //--------------------------------------------------------------------------
407 /// Bind vertex attribs.
408 ///
409 if (!vertex_desc_gles->BindVertexAttributes(
410 gl, vertex_buffer_view.range.offset)) {
411 return false;
412 }
413
414 //--------------------------------------------------------------------------
415 /// Bind uniform data.
416 ///
417 if (!vertex_desc_gles->BindUniformData(gl, //
418 *transients_allocator, //
419 command.vertex_bindings, //
420 command.fragment_bindings //
421 )) {
422 return false;
423 }
424
425 //--------------------------------------------------------------------------
426 /// Determine the primitive type.
427 ///
428 // GLES doesn't support setting the fill mode, so override the primitive
429 // with GL_LINE_STRIP to somewhat emulate PolygonMode::kLine. This isn't
430 // correct; full triangle outlines won't be drawn and disconnected
431 // geometry may appear connected. However this can still be useful for
432 // wireframe debug views.
433 auto mode = pipeline.GetDescriptor().GetPolygonMode() == PolygonMode::kLine
434 ? GL_LINE_STRIP
435 : ToMode(pipeline.GetDescriptor().GetPrimitiveType());
436
437 //--------------------------------------------------------------------------
438 /// Finally! Invoke the draw call.
439 ///
440 if (command.vertex_buffer.index_type == IndexType::kNone) {
441 gl.DrawArrays(mode, command.base_vertex,
442 command.vertex_buffer.vertex_count);
443 } else {
444 // Bind the index buffer if necessary.
445 auto index_buffer_view = command.vertex_buffer.index_buffer;
446 auto index_buffer = index_buffer_view.buffer;
447 const auto& index_buffer_gles = DeviceBufferGLES::Cast(*index_buffer);
448 if (!index_buffer_gles.BindAndUploadDataIfNecessary(
449 DeviceBufferGLES::BindingType::kElementArrayBuffer)) {
450 return false;
451 }
452 gl.DrawElements(mode, // mode
453 command.vertex_buffer.vertex_count, // count
454 ToIndexType(command.vertex_buffer.index_type), // type
455 reinterpret_cast<const GLvoid*>(static_cast<GLsizei>(
456 index_buffer_view.range.offset)) // indices
457 );
458 }
459
460 //--------------------------------------------------------------------------
461 /// Unbind vertex attribs.
462 ///
463 if (!vertex_desc_gles->UnbindVertexAttributes(gl)) {
464 return false;
465 }
466
467 //--------------------------------------------------------------------------
468 /// Unbind the program pipeline.
469 ///
470 if (!pipeline.UnbindProgram()) {
471 return false;
472 }
473 }
474
475 if (gl.DiscardFramebufferEXT.IsAvailable()) {
476 std::vector<GLenum> attachments;
477
478 // TODO(jonahwilliams): discarding stencil or depth on the default fbo
479 // causes Angle to discard the entire render target. Until we know the
480 // reason, default to storing.
481 bool angle_safe = gl.GetCapabilities()->IsANGLE() ? !is_default_fbo : true;
482
483 if (pass_data.discard_color_attachment) {
484 attachments.push_back(is_default_fbo ? GL_COLOR_EXT
485 : GL_COLOR_ATTACHMENT0);
486 }
487 if (pass_data.discard_depth_attachment && angle_safe) {
488 attachments.push_back(is_default_fbo ? GL_DEPTH_EXT
489 : GL_DEPTH_ATTACHMENT);
490 }
491
492 if (pass_data.discard_stencil_attachment && angle_safe) {
493 attachments.push_back(is_default_fbo ? GL_STENCIL_EXT
494 : GL_STENCIL_ATTACHMENT);
495 }
496 gl.DiscardFramebufferEXT(GL_FRAMEBUFFER, // target
497 attachments.size(), // attachments to discard
498 attachments.data() // size
499 );
500 }
501
502#ifdef IMPELLER_DEBUG
503 if (is_default_fbo) {
504 tracer->MarkFrameEnd(gl);
505 }
506#endif // IMPELLER_DEBUG
507
508 return true;
509}
510
511// |RenderPass|
512bool RenderPassGLES::OnEncodeCommands(const Context& context) const {
513 if (!IsValid()) {
514 return false;
515 }
516 const auto& render_target = GetRenderTarget();
517 if (!render_target.HasColorAttachment(0u)) {
518 return false;
519 }
520 const auto& color0 = render_target.GetColorAttachments().at(0u);
521 const auto& depth0 = render_target.GetDepthAttachment();
522 const auto& stencil0 = render_target.GetStencilAttachment();
523
524 auto pass_data = std::make_shared<RenderPassData>();
525 pass_data->label = label_;
526 pass_data->viewport.rect = Rect::MakeSize(GetRenderTargetSize());
527
528 //----------------------------------------------------------------------------
529 /// Setup color data.
530 ///
531 pass_data->color_attachment = color0.texture;
532 pass_data->clear_color = color0.clear_color;
533 pass_data->clear_color_attachment = CanClearAttachment(color0.load_action);
534 pass_data->discard_color_attachment =
535 CanDiscardAttachmentWhenDone(color0.store_action);
536
537 // When we are using EXT_multisampled_render_to_texture, it is implicitly
538 // resolved when we bind the texture to the framebuffer. We don't need to
539 // discard the attachment when we are done.
540 if (color0.resolve_texture) {
541 FML_DCHECK(context.GetCapabilities()->SupportsImplicitResolvingMSAA());
542 pass_data->discard_color_attachment = false;
543 }
544
545 //----------------------------------------------------------------------------
546 /// Setup depth data.
547 ///
548 if (depth0.has_value()) {
549 pass_data->depth_attachment = depth0->texture;
550 pass_data->clear_depth = depth0->clear_depth;
551 pass_data->clear_depth_attachment = CanClearAttachment(depth0->load_action);
552 pass_data->discard_depth_attachment =
553 CanDiscardAttachmentWhenDone(depth0->store_action);
554 }
555
556 //----------------------------------------------------------------------------
557 /// Setup stencil data.
558 ///
559 if (stencil0.has_value()) {
560 pass_data->stencil_attachment = stencil0->texture;
561 pass_data->clear_stencil = stencil0->clear_stencil;
562 pass_data->clear_stencil_attachment =
563 CanClearAttachment(stencil0->load_action);
564 pass_data->discard_stencil_attachment =
565 CanDiscardAttachmentWhenDone(stencil0->store_action);
566 }
567
568 std::shared_ptr<const RenderPassGLES> shared_this = shared_from_this();
569 auto tracer = ContextGLES::Cast(context).GetGPUTracer();
570 return reactor_->AddOperation([pass_data,
571 allocator = context.GetResourceAllocator(),
572 render_pass = std::move(shared_this),
573 tracer](const auto& reactor) {
574 auto result = EncodeCommandsInReactor(*pass_data, allocator, reactor,
575 render_pass->commands_, tracer);
576 FML_CHECK(result) << "Must be able to encode GL commands without error.";
577 });
578}
579
580} // namespace impeller
static const uint64_t kGreen
static const uint64_t kAlpha
static const uint64_t kBlue
static const uint64_t kRed
#define check(reporter, ref, unref, make, kill)
Definition: RefCntTest.cpp:85
Wraps a closure that is invoked in the destructor unless released by the caller.
Definition: closure.h:32
std::optional< StencilAttachmentDescriptor > GetBackStencilAttachmentDescriptor() const
std::optional< StencilAttachmentDescriptor > GetFrontStencilAttachmentDescriptor() const
The reactor attempts to make thread-safe usage of OpenGL ES easier to reason about.
Definition: reactor_gles.h:55
const ProcTableGLES & GetProcTable() const
Get the OpenGL proc. table the reactor uses to manage handles.
Definition: reactor_gles.cc:48
std::shared_ptr< ReactorGLES > Ref
Definition: reactor_gles.h:86
DlColor color
uint32_t * target
#define FML_DCHECK(condition)
Definition: logging.h:103
SK_API sk_sp< SkSurface > RenderTarget(GrRecordingContext *context, skgpu::Budgeted budgeted, const SkImageInfo &imageInfo, int sampleCount, GrSurfaceOrigin surfaceOrigin, const SkSurfaceProps *surfaceProps, bool shouldCreateWithMips=false, bool isProtected=false)
dictionary commands
Definition: dom.py:171
@ kNone
Definition: layer.h:53
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition: switches.h:228
constexpr bool CanClearAttachment(LoadAction action)
Definition: formats.h:239
float Scalar
Definition: scalar.h:18
constexpr GLenum ToIndexType(IndexType type)
Definition: formats_gles.h:33
constexpr GLenum ToCompareFunction(CompareFunction func)
Definition: formats_gles.h:68
std::string DebugToFramebufferError(int status)
Definition: formats_gles.cc:9
constexpr GLenum ToStencilOp(StencilOperation op)
Definition: formats_gles.h:46
bool EncodeCommandsInReactor(const RenderPassData &pass_data, const std::shared_ptr< Allocator > &transients_allocator, const ReactorGLES &reactor, const std::vector< Command > &commands, const std::shared_ptr< GPUTracerGLES > &tracer)
constexpr GLenum ToMode(PrimitiveType primitive_type)
Definition: formats_gles.h:17
void ConfigureBlending(const ProcTableGLES &gl, const ColorAttachmentDescriptor *color)
void ConfigureStencil(const ProcTableGLES &gl, const PipelineDescriptor &pipeline, uint32_t stencil_reference)
constexpr GLenum ToBlendFactor(BlendFactor factor)
Definition: formats_gles.h:90
constexpr GLenum ToBlendOperation(BlendOperation op)
Definition: formats_gles.h:126
constexpr bool CanDiscardAttachmentWhenDone(StoreAction action)
Definition: formats.h:250
gl
Definition: malisc.py:41
Definition: ref_ptr.h:256
list command
Definition: valgrind.py:24
Describe the color attachment that will be used with this pipeline.
Definition: formats.h:512
Scalar blue
Definition: color.h:138
Scalar alpha
Definition: color.h:143
Scalar red
Definition: color.h:128
Scalar green
Definition: color.h:133
Encapsulates data that will be needed in the reactor for the encoding of commands for this render pas...
std::shared_ptr< Texture > depth_attachment
std::shared_ptr< Texture > color_attachment
std::shared_ptr< Texture > stencil_attachment
StencilOperation depth_stencil_pass
Definition: formats.h:623
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:131
#define VALIDATION_LOG
Definition: validation.h:73