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->isWebGL() || mState.colorAttachmentsAreUniqueImages());
267 if (!context->isWebGL())
268 {
269 if (!mState.colorAttachmentsAreUniqueImages())
270 {
271 return gl::FramebufferStatus::Incomplete(
272 GL_FRAMEBUFFER_UNSUPPORTED,
273 gl::err::kFramebufferIncompleteUnsupportedNonUniqueAttachments);
274 }
275 }
276
277 // D3D requires all render targets to have the same dimensions.
278 if (!mState.attachmentsHaveSameDimensions())
279 {
280 return gl::FramebufferStatus::Incomplete(
281 GL_FRAMEBUFFER_UNSUPPORTED,
282 gl::err::kFramebufferIncompleteUnsupportedMissmatchedDimensions);
283 }
284
285 return gl::FramebufferStatus::Complete();
286 }
287
syncState(const gl::Context * context,GLenum binding,const gl::Framebuffer::DirtyBits & dirtyBits,gl::Command command)288 angle::Result FramebufferD3D::syncState(const gl::Context *context,
289 GLenum binding,
290 const gl::Framebuffer::DirtyBits &dirtyBits,
291 gl::Command command)
292 {
293 if (!mColorAttachmentsForRender.valid())
294 {
295 return angle::Result::Continue;
296 }
297
298 for (auto dirtyBit : dirtyBits)
299 {
300 if ((dirtyBit >= gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 &&
301 dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX) ||
302 dirtyBit == gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS)
303 {
304 mColorAttachmentsForRender.reset();
305 }
306 }
307
308 return angle::Result::Continue;
309 }
310
getColorAttachmentsForRender(const gl::Context * context)311 const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender(const gl::Context *context)
312 {
313 gl::DrawBufferMask activeProgramOutputs =
314 context->getState().getProgram()->getExecutable().getActiveOutputVariablesMask();
315
316 if (mColorAttachmentsForRender.valid() && mCurrentActiveProgramOutputs == activeProgramOutputs)
317 {
318 return mColorAttachmentsForRender.value();
319 }
320
321 // Does not actually free memory
322 gl::AttachmentList colorAttachmentsForRender;
323 mColorAttachmentsForRenderMask.reset();
324
325 const auto &colorAttachments = mState.getColorAttachments();
326 const auto &drawBufferStates = mState.getDrawBufferStates();
327 const auto &features = mRenderer->getFeatures();
328
329 for (size_t attachmentIndex = 0; attachmentIndex < colorAttachments.size(); ++attachmentIndex)
330 {
331 GLenum drawBufferState = drawBufferStates[attachmentIndex];
332 const gl::FramebufferAttachment &colorAttachment = colorAttachments[attachmentIndex];
333
334 if (colorAttachment.isAttached() && drawBufferState != GL_NONE &&
335 activeProgramOutputs[attachmentIndex])
336 {
337 ASSERT(drawBufferState == GL_BACK ||
338 drawBufferState == (GL_COLOR_ATTACHMENT0_EXT + attachmentIndex));
339 colorAttachmentsForRender.push_back(&colorAttachment);
340 mColorAttachmentsForRenderMask.set(attachmentIndex);
341 }
342 else if (!features.mrtPerfWorkaround.enabled)
343 {
344 colorAttachmentsForRender.push_back(nullptr);
345 mColorAttachmentsForRenderMask.set(attachmentIndex);
346 }
347 }
348
349 // When rendering with no render target on D3D, two bugs lead to incorrect behavior on Intel
350 // drivers < 4815. The rendering samples always pass neglecting discard statements in pixel
351 // shader. We add a mock texture as render target in such case.
352 if (mRenderer->getFeatures().addMockTextureNoRenderTarget.enabled &&
353 colorAttachmentsForRender.empty() && activeProgramOutputs.any())
354 {
355 static_assert(static_cast<size_t>(activeProgramOutputs.size()) <= 32,
356 "Size of active program outputs should less or equal than 32.");
357 const GLuint activeProgramLocation = static_cast<GLuint>(
358 gl::ScanForward(static_cast<uint32_t>(activeProgramOutputs.bits())));
359
360 if (mMockAttachment.isAttached() &&
361 (mMockAttachment.getBinding() - GL_COLOR_ATTACHMENT0) == activeProgramLocation)
362 {
363 colorAttachmentsForRender.push_back(&mMockAttachment);
364 }
365 else
366 {
367 // Remove mock attachment to prevents us from leaking it, and the program may require
368 // it to be attached to a new binding point.
369 if (mMockAttachment.isAttached())
370 {
371 mMockAttachment.detach(context, Serial());
372 }
373
374 gl::Texture *mockTex = nullptr;
375 // TODO(jmadill): Handle error if mock texture can't be created.
376 (void)mRenderer->getIncompleteTexture(context, gl::TextureType::_2D, &mockTex);
377 if (mockTex)
378 {
379 gl::ImageIndex index = gl::ImageIndex::Make2D(0);
380 mMockAttachment = gl::FramebufferAttachment(
381 context, GL_TEXTURE, GL_COLOR_ATTACHMENT0_EXT + activeProgramLocation, index,
382 mockTex, Serial());
383 colorAttachmentsForRender.push_back(&mMockAttachment);
384 }
385 }
386 }
387
388 mColorAttachmentsForRender = std::move(colorAttachmentsForRender);
389 mCurrentActiveProgramOutputs = activeProgramOutputs;
390
391 return mColorAttachmentsForRender.value();
392 }
393
destroy(const gl::Context * context)394 void FramebufferD3D::destroy(const gl::Context *context)
395 {
396 if (mMockAttachment.isAttached())
397 {
398 mMockAttachment.detach(context, Serial());
399 }
400 }
401
402 } // namespace rx
403