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