1 //
2 // Copyright 2014 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // FramebufferD3D.cpp: Implements the DefaultAttachmentD3D and FramebufferD3D classes.
8
9 #include "libANGLE/renderer/d3d/FramebufferD3D.h"
10
11 #include "common/bitset_utils.h"
12 #include "libANGLE/Context.h"
13 #include "libANGLE/Framebuffer.h"
14 #include "libANGLE/FramebufferAttachment.h"
15 #include "libANGLE/Surface.h"
16 #include "libANGLE/formatutils.h"
17 #include "libANGLE/renderer/ContextImpl.h"
18 #include "libANGLE/renderer/d3d/ContextD3D.h"
19 #include "libANGLE/renderer/d3d/RenderTargetD3D.h"
20 #include "libANGLE/renderer/d3d/RenderbufferD3D.h"
21 #include "libANGLE/renderer/d3d/RendererD3D.h"
22 #include "libANGLE/renderer/d3d/SurfaceD3D.h"
23 #include "libANGLE/renderer/d3d/SwapChainD3D.h"
24 #include "libANGLE/renderer/d3d/TextureD3D.h"
25
26 namespace rx
27 {
28
29 namespace
30 {
31
GetClearParameters(const gl::State & state,GLbitfield mask)32 ClearParameters GetClearParameters(const gl::State &state, GLbitfield mask)
33 {
34 ClearParameters clearParams;
35 memset(&clearParams, 0, sizeof(ClearParameters));
36
37 clearParams.colorF = state.getColorClearValue();
38 clearParams.colorType = GL_FLOAT;
39 clearParams.clearDepth = false;
40 clearParams.depthValue = state.getDepthClearValue();
41 clearParams.clearStencil = false;
42 clearParams.stencilValue = state.getStencilClearValue();
43 clearParams.stencilWriteMask = state.getDepthStencilState().stencilWritemask;
44
45 const auto *framebufferObject = state.getDrawFramebuffer();
46 const gl::Extents &framebufferSize = framebufferObject->getFirstNonNullAttachment()->getSize();
47 const gl::Offset &surfaceTextureOffset = framebufferObject->getSurfaceTextureOffset();
48 if (state.isScissorTestEnabled())
49 {
50 clearParams.scissorEnabled = true;
51 clearParams.scissor = state.getScissor();
52 clearParams.scissor.x = clearParams.scissor.x + surfaceTextureOffset.x;
53 clearParams.scissor.y = clearParams.scissor.y + surfaceTextureOffset.y;
54 }
55 else if (surfaceTextureOffset != gl::kOffsetZero)
56 {
57 clearParams.scissorEnabled = true;
58 clearParams.scissor = gl::Rectangle(surfaceTextureOffset.x, surfaceTextureOffset.y,
59 framebufferSize.width, framebufferSize.height);
60 }
61
62 const bool clearColor =
63 (mask & GL_COLOR_BUFFER_BIT) && framebufferObject->hasEnabledDrawBuffer();
64 if (clearColor)
65 {
66 clearParams.clearColor.set();
67 }
68 else
69 {
70 clearParams.clearColor.reset();
71 }
72 clearParams.colorMask = state.getBlendStateExt().mColorMask;
73
74 if (mask & GL_DEPTH_BUFFER_BIT)
75 {
76 if (state.getDepthStencilState().depthMask &&
77 framebufferObject->getDepthAttachment() != nullptr)
78 {
79 clearParams.clearDepth = true;
80 }
81 }
82
83 if (mask & GL_STENCIL_BUFFER_BIT)
84 {
85 if (framebufferObject->getStencilAttachment() != nullptr &&
86 framebufferObject->getStencilAttachment()->getStencilSize() > 0)
87 {
88 clearParams.clearStencil = true;
89 }
90 }
91
92 return clearParams;
93 }
94 } // namespace
95
96 ClearParameters::ClearParameters() = default;
97
98 ClearParameters::ClearParameters(const ClearParameters &other) = default;
99
FramebufferD3D(const gl::FramebufferState & data,RendererD3D * renderer)100 FramebufferD3D::FramebufferD3D(const gl::FramebufferState &data, RendererD3D *renderer)
101 : FramebufferImpl(data), mRenderer(renderer), mDummyAttachment()
102 {}
103
~FramebufferD3D()104 FramebufferD3D::~FramebufferD3D() {}
105
clear(const gl::Context * context,GLbitfield mask)106 angle::Result FramebufferD3D::clear(const gl::Context *context, GLbitfield mask)
107 {
108 ClearParameters clearParams = GetClearParameters(context->getState(), mask);
109 return clearImpl(context, clearParams);
110 }
111
clearBufferfv(const gl::Context * context,GLenum buffer,GLint drawbuffer,const GLfloat * values)112 angle::Result FramebufferD3D::clearBufferfv(const gl::Context *context,
113 GLenum buffer,
114 GLint drawbuffer,
115 const GLfloat *values)
116 {
117 // glClearBufferfv can be called to clear the color buffer or depth buffer
118 ClearParameters clearParams = GetClearParameters(context->getState(), 0);
119
120 if (buffer == GL_COLOR)
121 {
122 for (unsigned int i = 0; i < clearParams.clearColor.size(); i++)
123 {
124 clearParams.clearColor[i] = (drawbuffer == static_cast<int>(i));
125 }
126 clearParams.colorF = gl::ColorF(values[0], values[1], values[2], values[3]);
127 clearParams.colorType = GL_FLOAT;
128 }
129
130 if (buffer == GL_DEPTH)
131 {
132 clearParams.clearDepth = true;
133 clearParams.depthValue = values[0];
134 }
135
136 return clearImpl(context, clearParams);
137 }
138
clearBufferuiv(const gl::Context * context,GLenum buffer,GLint drawbuffer,const GLuint * values)139 angle::Result FramebufferD3D::clearBufferuiv(const gl::Context *context,
140 GLenum buffer,
141 GLint drawbuffer,
142 const GLuint *values)
143 {
144 // glClearBufferuiv can only be called to clear a color buffer
145 ClearParameters clearParams = GetClearParameters(context->getState(), 0);
146 for (unsigned int i = 0; i < clearParams.clearColor.size(); i++)
147 {
148 clearParams.clearColor[i] = (drawbuffer == static_cast<int>(i));
149 }
150 clearParams.colorUI = gl::ColorUI(values[0], values[1], values[2], values[3]);
151 clearParams.colorType = GL_UNSIGNED_INT;
152
153 return clearImpl(context, clearParams);
154 }
155
clearBufferiv(const gl::Context * context,GLenum buffer,GLint drawbuffer,const GLint * values)156 angle::Result FramebufferD3D::clearBufferiv(const gl::Context *context,
157 GLenum buffer,
158 GLint drawbuffer,
159 const GLint *values)
160 {
161 // glClearBufferiv can be called to clear the color buffer or stencil buffer
162 ClearParameters clearParams = GetClearParameters(context->getState(), 0);
163
164 if (buffer == GL_COLOR)
165 {
166 for (unsigned int i = 0; i < clearParams.clearColor.size(); i++)
167 {
168 clearParams.clearColor[i] = (drawbuffer == static_cast<int>(i));
169 }
170 clearParams.colorI = gl::ColorI(values[0], values[1], values[2], values[3]);
171 clearParams.colorType = GL_INT;
172 }
173
174 if (buffer == GL_STENCIL)
175 {
176 clearParams.clearStencil = true;
177 clearParams.stencilValue = values[0];
178 }
179
180 return clearImpl(context, clearParams);
181 }
182
clearBufferfi(const gl::Context * context,GLenum buffer,GLint drawbuffer,GLfloat depth,GLint stencil)183 angle::Result FramebufferD3D::clearBufferfi(const gl::Context *context,
184 GLenum buffer,
185 GLint drawbuffer,
186 GLfloat depth,
187 GLint stencil)
188 {
189 // glClearBufferfi can only be called to clear a depth stencil buffer
190 ClearParameters clearParams = GetClearParameters(context->getState(), 0);
191 clearParams.clearDepth = true;
192 clearParams.depthValue = depth;
193 clearParams.clearStencil = true;
194 clearParams.stencilValue = stencil;
195
196 return clearImpl(context, clearParams);
197 }
198
readPixels(const gl::Context * context,const gl::Rectangle & area,GLenum format,GLenum type,void * pixels)199 angle::Result FramebufferD3D::readPixels(const gl::Context *context,
200 const gl::Rectangle &area,
201 GLenum format,
202 GLenum type,
203 void *pixels)
204 {
205 // Clip read area to framebuffer.
206 const gl::Extents fbSize = getState().getReadPixelsAttachment(format)->getSize();
207 const gl::Rectangle fbRect(0, 0, fbSize.width, fbSize.height);
208 gl::Rectangle clippedArea;
209 if (!ClipRectangle(area, fbRect, &clippedArea))
210 {
211 // nothing to read
212 return angle::Result::Continue;
213 }
214
215 const gl::PixelPackState &packState = context->getState().getPackState();
216
217 const gl::InternalFormat &sizedFormatInfo = gl::GetInternalFormatInfo(format, type);
218
219 ContextD3D *contextD3D = GetImplAs<ContextD3D>(context);
220
221 GLuint outputPitch = 0;
222 ANGLE_CHECK_GL_MATH(contextD3D,
223 sizedFormatInfo.computeRowPitch(type, area.width, packState.alignment,
224 packState.rowLength, &outputPitch));
225
226 GLuint outputSkipBytes = 0;
227 ANGLE_CHECK_GL_MATH(contextD3D, sizedFormatInfo.computeSkipBytes(
228 type, outputPitch, 0, packState, false, &outputSkipBytes));
229 outputSkipBytes += (clippedArea.x - area.x) * sizedFormatInfo.pixelBytes +
230 (clippedArea.y - area.y) * outputPitch;
231
232 return readPixelsImpl(context, clippedArea, format, type, outputPitch, packState,
233 static_cast<uint8_t *>(pixels) + outputSkipBytes);
234 }
235
blit(const gl::Context * context,const gl::Rectangle & sourceArea,const gl::Rectangle & destArea,GLbitfield mask,GLenum filter)236 angle::Result FramebufferD3D::blit(const gl::Context *context,
237 const gl::Rectangle &sourceArea,
238 const gl::Rectangle &destArea,
239 GLbitfield mask,
240 GLenum filter)
241 {
242 const auto &glState = context->getState();
243 const gl::Framebuffer *sourceFramebuffer = glState.getReadFramebuffer();
244 const gl::Rectangle *scissor = glState.isScissorTestEnabled() ? &glState.getScissor() : nullptr;
245 ANGLE_TRY(blitImpl(context, sourceArea, destArea, scissor, (mask & GL_COLOR_BUFFER_BIT) != 0,
246 (mask & GL_DEPTH_BUFFER_BIT) != 0, (mask & GL_STENCIL_BUFFER_BIT) != 0,
247 filter, sourceFramebuffer));
248
249 return angle::Result::Continue;
250 }
251
checkStatus(const gl::Context * context) const252 bool FramebufferD3D::checkStatus(const gl::Context *context) const
253 {
254 // if we have both a depth and stencil buffer, they must refer to the same object
255 // since we only support packed_depth_stencil and not separate depth and stencil
256 if (mState.hasSeparateDepthAndStencilAttachments())
257 {
258 return false;
259 }
260
261 // D3D11 does not allow for overlapping RenderTargetViews.
262 // If WebGL compatibility is enabled, this has already been checked at a higher level.
263 ASSERT(!context->getExtensions().webglCompatibility ||
264 mState.colorAttachmentsAreUniqueImages());
265 if (!context->getExtensions().webglCompatibility)
266 {
267 if (!mState.colorAttachmentsAreUniqueImages())
268 {
269 return false;
270 }
271 }
272
273 // D3D requires all render targets to have the same dimensions.
274 if (!mState.attachmentsHaveSameDimensions())
275 {
276 return false;
277 }
278
279 return true;
280 }
281
syncState(const gl::Context * context,GLenum binding,const gl::Framebuffer::DirtyBits & dirtyBits)282 angle::Result FramebufferD3D::syncState(const gl::Context *context,
283 GLenum binding,
284 const gl::Framebuffer::DirtyBits &dirtyBits)
285 {
286 if (!mColorAttachmentsForRender.valid())
287 {
288 return angle::Result::Continue;
289 }
290
291 for (auto dirtyBit : dirtyBits)
292 {
293 if ((dirtyBit >= gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 &&
294 dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX) ||
295 dirtyBit == gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS)
296 {
297 mColorAttachmentsForRender.reset();
298 }
299 }
300
301 return angle::Result::Continue;
302 }
303
getColorAttachmentsForRender(const gl::Context * context)304 const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender(const gl::Context *context)
305 {
306 gl::DrawBufferMask activeProgramOutputs =
307 context->getState().getProgram()->getActiveOutputVariables();
308
309 if (mColorAttachmentsForRender.valid() && mCurrentActiveProgramOutputs == activeProgramOutputs)
310 {
311 return mColorAttachmentsForRender.value();
312 }
313
314 // Does not actually free memory
315 gl::AttachmentList colorAttachmentsForRender;
316 mColorAttachmentsForRenderMask.reset();
317
318 const auto &colorAttachments = mState.getColorAttachments();
319 const auto &drawBufferStates = mState.getDrawBufferStates();
320 const auto &features = mRenderer->getFeatures();
321
322 for (size_t attachmentIndex = 0; attachmentIndex < colorAttachments.size(); ++attachmentIndex)
323 {
324 GLenum drawBufferState = drawBufferStates[attachmentIndex];
325 const gl::FramebufferAttachment &colorAttachment = colorAttachments[attachmentIndex];
326
327 if (colorAttachment.isAttached() && drawBufferState != GL_NONE &&
328 activeProgramOutputs[attachmentIndex])
329 {
330 ASSERT(drawBufferState == GL_BACK ||
331 drawBufferState == (GL_COLOR_ATTACHMENT0_EXT + attachmentIndex));
332 colorAttachmentsForRender.push_back(&colorAttachment);
333 mColorAttachmentsForRenderMask.set(attachmentIndex);
334 }
335 else if (!features.mrtPerfWorkaround.enabled)
336 {
337 colorAttachmentsForRender.push_back(nullptr);
338 mColorAttachmentsForRenderMask.set(attachmentIndex);
339 }
340 }
341
342 // When rendering with no render target on D3D, two bugs lead to incorrect behavior on Intel
343 // drivers < 4815. The rendering samples always pass neglecting discard statements in pixel
344 // shader. We add a dummy texture as render target in such case.
345 if (mRenderer->getFeatures().addDummyTextureNoRenderTarget.enabled &&
346 colorAttachmentsForRender.empty() && activeProgramOutputs.any())
347 {
348 static_assert(static_cast<size_t>(activeProgramOutputs.size()) <= 32,
349 "Size of active program outputs should less or equal than 32.");
350 const GLuint activeProgramLocation = static_cast<GLuint>(
351 gl::ScanForward(static_cast<uint32_t>(activeProgramOutputs.bits())));
352
353 if (mDummyAttachment.isAttached() &&
354 (mDummyAttachment.getBinding() - GL_COLOR_ATTACHMENT0) == activeProgramLocation)
355 {
356 colorAttachmentsForRender.push_back(&mDummyAttachment);
357 }
358 else
359 {
360 // Remove dummy attachment to prevents us from leaking it, and the program may require
361 // it to be attached to a new binding point.
362 if (mDummyAttachment.isAttached())
363 {
364 mDummyAttachment.detach(context);
365 }
366
367 gl::Texture *dummyTex = nullptr;
368 // TODO(Jamie): Handle error if dummy texture can't be created.
369 (void)mRenderer->getIncompleteTexture(context, gl::TextureType::_2D, &dummyTex);
370 if (dummyTex)
371 {
372
373 gl::ImageIndex index = gl::ImageIndex::Make2D(0);
374 mDummyAttachment = gl::FramebufferAttachment(
375 context, GL_TEXTURE, GL_COLOR_ATTACHMENT0_EXT + activeProgramLocation, index,
376 dummyTex);
377 colorAttachmentsForRender.push_back(&mDummyAttachment);
378 }
379 }
380 }
381
382 mColorAttachmentsForRender = std::move(colorAttachmentsForRender);
383 mCurrentActiveProgramOutputs = activeProgramOutputs;
384
385 return mColorAttachmentsForRender.value();
386 }
387
destroy(const gl::Context * context)388 void FramebufferD3D::destroy(const gl::Context *context)
389 {
390 if (mDummyAttachment.isAttached())
391 {
392 mDummyAttachment.detach(context);
393 }
394 }
395
396 } // namespace rx
397