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 // Framebuffer11.cpp: Implements the Framebuffer11 class.
8 
9 #include "libANGLE/renderer/d3d/d3d11/Framebuffer11.h"
10 
11 #include "common/bitset_utils.h"
12 #include "common/debug.h"
13 #include "libANGLE/Context.h"
14 #include "libANGLE/Framebuffer.h"
15 #include "libANGLE/FramebufferAttachment.h"
16 #include "libANGLE/Texture.h"
17 #include "libANGLE/renderer/d3d/TextureD3D.h"
18 #include "libANGLE/renderer/d3d/d3d11/Buffer11.h"
19 #include "libANGLE/renderer/d3d/d3d11/Clear11.h"
20 #include "libANGLE/renderer/d3d/d3d11/RenderTarget11.h"
21 #include "libANGLE/renderer/d3d/d3d11/Renderer11.h"
22 #include "libANGLE/renderer/d3d/d3d11/TextureStorage11.h"
23 #include "libANGLE/renderer/d3d/d3d11/formatutils11.h"
24 #include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
25 
26 using namespace angle;
27 
28 namespace rx
29 {
30 
31 namespace
32 {
MarkAttachmentsDirty(const gl::Context * context,const gl::FramebufferAttachment * attachment)33 gl::Error MarkAttachmentsDirty(const gl::Context *context,
34                                const gl::FramebufferAttachment *attachment)
35 {
36     if (attachment->type() == GL_TEXTURE)
37     {
38         gl::Texture *texture = attachment->getTexture();
39 
40         TextureD3D *textureD3D = GetImplAs<TextureD3D>(texture);
41 
42         TextureStorage *texStorage = nullptr;
43         ANGLE_TRY(textureD3D->getNativeTexture(context, &texStorage));
44 
45         if (texStorage)
46         {
47             TextureStorage11 *texStorage11 = GetAs<TextureStorage11>(texStorage);
48             ASSERT(texStorage11);
49 
50             texStorage11->markLevelDirty(attachment->mipLevel());
51         }
52     }
53 
54     return gl::NoError();
55 }
56 
UpdateCachedRenderTarget(const gl::Context * context,const gl::FramebufferAttachment * attachment,RenderTarget11 * & cachedRenderTarget,OnRenderTargetDirtyBinding * channelBinding)57 void UpdateCachedRenderTarget(const gl::Context *context,
58                               const gl::FramebufferAttachment *attachment,
59                               RenderTarget11 *&cachedRenderTarget,
60                               OnRenderTargetDirtyBinding *channelBinding)
61 {
62     RenderTarget11 *newRenderTarget = nullptr;
63     if (attachment)
64     {
65         // TODO(jmadill): Don't swallow this error.
66         gl::Error error = attachment->getRenderTarget(context, &newRenderTarget);
67         if (error.isError())
68         {
69             ERR() << "Internal rendertarget error: " << error;
70         }
71     }
72     if (newRenderTarget != cachedRenderTarget)
73     {
74         OnRenderTargetDirtyChannel *channel =
75             (newRenderTarget ? newRenderTarget->getBroadcastChannel() : nullptr);
76         channelBinding->bind(channel);
77         cachedRenderTarget = newRenderTarget;
78     }
79 }
80 }  // anonymous namespace
81 
Framebuffer11(const gl::FramebufferState & data,Renderer11 * renderer)82 Framebuffer11::Framebuffer11(const gl::FramebufferState &data, Renderer11 *renderer)
83     : FramebufferD3D(data, renderer),
84       mRenderer(renderer),
85       mCachedDepthStencilRenderTarget(nullptr),
86       mDepthStencilRenderTargetDirty(this, gl::IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS)
87 {
88     ASSERT(mRenderer != nullptr);
89     mCachedColorRenderTargets.fill(nullptr);
90     for (size_t colorIndex = 0; colorIndex < data.getColorAttachments().size(); ++colorIndex)
91     {
92         mColorRenderTargetsDirty.emplace_back(this, colorIndex);
93     }
94 }
95 
~Framebuffer11()96 Framebuffer11::~Framebuffer11()
97 {
98 }
99 
markAttachmentsDirty(const gl::Context * context) const100 gl::Error Framebuffer11::markAttachmentsDirty(const gl::Context *context) const
101 {
102     const auto &colorAttachments = mState.getColorAttachments();
103     for (size_t drawBuffer : mState.getEnabledDrawBuffers())
104     {
105         const gl::FramebufferAttachment &colorAttachment = colorAttachments[drawBuffer];
106         ASSERT(colorAttachment.isAttached());
107         ANGLE_TRY(MarkAttachmentsDirty(context, &colorAttachment));
108     }
109 
110     const gl::FramebufferAttachment *dsAttachment = mState.getDepthOrStencilAttachment();
111     if (dsAttachment)
112     {
113         ANGLE_TRY(MarkAttachmentsDirty(context, dsAttachment));
114     }
115 
116     return gl::NoError();
117 }
118 
clearImpl(const gl::Context * context,const ClearParameters & clearParams)119 gl::Error Framebuffer11::clearImpl(const gl::Context *context, const ClearParameters &clearParams)
120 {
121     Clear11 *clearer = mRenderer->getClearer();
122 
123     const gl::FramebufferAttachment *colorAttachment = mState.getFirstColorAttachment();
124     if (clearParams.scissorEnabled == true && colorAttachment != nullptr &&
125         UsePresentPathFast(mRenderer, colorAttachment))
126     {
127         // If the current framebuffer is using the default colorbuffer, and present path fast is
128         // active, and the scissor rect is enabled, then we should invert the scissor rect
129         // vertically
130         ClearParameters presentPathFastClearParams = clearParams;
131         gl::Extents framebufferSize                = colorAttachment->getSize();
132         presentPathFastClearParams.scissor.y       = framebufferSize.height -
133                                                presentPathFastClearParams.scissor.y -
134                                                presentPathFastClearParams.scissor.height;
135         ANGLE_TRY(clearer->clearFramebuffer(context, presentPathFastClearParams, mState));
136     }
137     else
138     {
139         ANGLE_TRY(clearer->clearFramebuffer(context, clearParams, mState));
140     }
141 
142     ANGLE_TRY(markAttachmentsDirty(context));
143 
144     return gl::NoError();
145 }
146 
invalidate(const gl::Context * context,size_t count,const GLenum * attachments)147 gl::Error Framebuffer11::invalidate(const gl::Context *context,
148                                     size_t count,
149                                     const GLenum *attachments)
150 {
151     return invalidateBase(context, count, attachments, false);
152 }
153 
discard(const gl::Context * context,size_t count,const GLenum * attachments)154 gl::Error Framebuffer11::discard(const gl::Context *context,
155                                  size_t count,
156                                  const GLenum *attachments)
157 {
158     return invalidateBase(context, count, attachments, true);
159 }
160 
invalidateBase(const gl::Context * context,size_t count,const GLenum * attachments,bool useEXTBehavior) const161 gl::Error Framebuffer11::invalidateBase(const gl::Context *context,
162                                         size_t count,
163                                         const GLenum *attachments,
164                                         bool useEXTBehavior) const
165 {
166     ID3D11DeviceContext1 *deviceContext1 = mRenderer->getDeviceContext1IfSupported();
167 
168     if (!deviceContext1)
169     {
170         // DiscardView() is only supported on ID3D11DeviceContext1
171         return gl::NoError();
172     }
173 
174     bool foundDepth = false;
175     bool foundStencil = false;
176 
177     for (size_t i = 0; i < count; ++i)
178     {
179         switch (attachments[i])
180         {
181           // Handle depth and stencil attachments. Defer discarding until later.
182           case GL_DEPTH_STENCIL_ATTACHMENT:
183             foundDepth = true;
184             foundStencil = true;
185             break;
186           case GL_DEPTH_EXT:
187           case GL_DEPTH_ATTACHMENT:
188             foundDepth = true;
189             break;
190           case GL_STENCIL_EXT:
191           case GL_STENCIL_ATTACHMENT:
192             foundStencil = true;
193             break;
194           default:
195             {
196                 // Handle color attachments
197                 ASSERT((attachments[i] >= GL_COLOR_ATTACHMENT0 && attachments[i] <= GL_COLOR_ATTACHMENT15) ||
198                        (attachments[i] == GL_COLOR));
199 
200                 size_t colorIndex =
201                     (attachments[i] == GL_COLOR ? 0u : (attachments[i] - GL_COLOR_ATTACHMENT0));
202                 const gl::FramebufferAttachment *colorAttachment =
203                     mState.getColorAttachment(colorIndex);
204                 if (colorAttachment)
205                 {
206                     ANGLE_TRY(invalidateAttachment(context, colorAttachment));
207                 }
208                 break;
209             }
210         }
211     }
212 
213     bool discardDepth = false;
214     bool discardStencil = false;
215 
216     // The D3D11 renderer uses the same view for depth and stencil buffers, so we must be careful.
217     if (useEXTBehavior)
218     {
219         // In the extension, if the app discards only one of the depth and stencil attachments, but
220         // those are backed by the same packed_depth_stencil buffer, then both images become undefined.
221         discardDepth = foundDepth;
222 
223         // Don't bother discarding the stencil buffer if the depth buffer will already do it
224         discardStencil = foundStencil && (!discardDepth || mState.getDepthAttachment() == nullptr);
225     }
226     else
227     {
228         // In ES 3.0.4, if a specified attachment has base internal format DEPTH_STENCIL but the
229         // attachments list does not include DEPTH_STENCIL_ATTACHMENT or both DEPTH_ATTACHMENT and
230         // STENCIL_ATTACHMENT, then only the specified portion of every pixel in the subregion of pixels
231         // of the DEPTH_STENCIL buffer may be invalidated, and the other portion must be preserved.
232         discardDepth = (foundDepth && foundStencil) ||
233                        (foundDepth && (mState.getStencilAttachment() == nullptr));
234         discardStencil = (foundStencil && (mState.getDepthAttachment() == nullptr));
235     }
236 
237     if (discardDepth && mState.getDepthAttachment())
238     {
239         ANGLE_TRY(invalidateAttachment(context, mState.getDepthAttachment()));
240     }
241 
242     if (discardStencil && mState.getStencilAttachment())
243     {
244         ANGLE_TRY(invalidateAttachment(context, mState.getStencilAttachment()));
245     }
246 
247     return gl::NoError();
248 }
249 
invalidateSub(const gl::Context * context,size_t,const GLenum *,const gl::Rectangle &)250 gl::Error Framebuffer11::invalidateSub(const gl::Context *context,
251                                        size_t,
252                                        const GLenum *,
253                                        const gl::Rectangle &)
254 {
255     // A no-op implementation conforms to the spec, so don't call UNIMPLEMENTED()
256     return gl::NoError();
257 }
258 
invalidateAttachment(const gl::Context * context,const gl::FramebufferAttachment * attachment) const259 gl::Error Framebuffer11::invalidateAttachment(const gl::Context *context,
260                                               const gl::FramebufferAttachment *attachment) const
261 {
262     ID3D11DeviceContext1 *deviceContext1 = mRenderer->getDeviceContext1IfSupported();
263     ASSERT(deviceContext1);
264     ASSERT(attachment && attachment->isAttached());
265 
266     RenderTarget11 *renderTarget = nullptr;
267     ANGLE_TRY(attachment->getRenderTarget(context, &renderTarget));
268     const auto &rtv = renderTarget->getRenderTargetView();
269 
270     if (rtv.valid())
271     {
272         deviceContext1->DiscardView(rtv.get());
273     }
274 
275     return gl::NoError();
276 }
277 
readPixelsImpl(const gl::Context * context,const gl::Rectangle & area,GLenum format,GLenum type,size_t outputPitch,const gl::PixelPackState & pack,uint8_t * pixels)278 gl::Error Framebuffer11::readPixelsImpl(const gl::Context *context,
279                                         const gl::Rectangle &area,
280                                         GLenum format,
281                                         GLenum type,
282                                         size_t outputPitch,
283                                         const gl::PixelPackState &pack,
284                                         uint8_t *pixels)
285 {
286     const gl::FramebufferAttachment *readAttachment = mState.getReadAttachment();
287     ASSERT(readAttachment);
288 
289     gl::Buffer *packBuffer = context->getGLState().getTargetBuffer(gl::BufferBinding::PixelPack);
290     if (packBuffer != nullptr)
291     {
292         Buffer11 *packBufferStorage = GetImplAs<Buffer11>(packBuffer);
293         PackPixelsParams packParams(area, format, type, static_cast<GLuint>(outputPitch), pack,
294                                     packBuffer, reinterpret_cast<ptrdiff_t>(pixels));
295 
296         return packBufferStorage->packPixels(context, *readAttachment, packParams);
297     }
298 
299     return mRenderer->readFromAttachment(context, *readAttachment, area, format, type,
300                                          static_cast<GLuint>(outputPitch), pack, pixels);
301 }
302 
blitImpl(const gl::Context * context,const gl::Rectangle & sourceArea,const gl::Rectangle & destArea,const gl::Rectangle * scissor,bool blitRenderTarget,bool blitDepth,bool blitStencil,GLenum filter,const gl::Framebuffer * sourceFramebuffer)303 gl::Error Framebuffer11::blitImpl(const gl::Context *context,
304                                   const gl::Rectangle &sourceArea,
305                                   const gl::Rectangle &destArea,
306                                   const gl::Rectangle *scissor,
307                                   bool blitRenderTarget,
308                                   bool blitDepth,
309                                   bool blitStencil,
310                                   GLenum filter,
311                                   const gl::Framebuffer *sourceFramebuffer)
312 {
313     if (blitRenderTarget)
314     {
315         const gl::FramebufferAttachment *readBuffer = sourceFramebuffer->getReadColorbuffer();
316         ASSERT(readBuffer);
317 
318         RenderTargetD3D *readRenderTarget = nullptr;
319         ANGLE_TRY(readBuffer->getRenderTarget(context, &readRenderTarget));
320         ASSERT(readRenderTarget);
321 
322         const auto &colorAttachments = mState.getColorAttachments();
323         const auto &drawBufferStates = mState.getDrawBufferStates();
324 
325         for (size_t colorAttachment = 0; colorAttachment < colorAttachments.size(); colorAttachment++)
326         {
327             const gl::FramebufferAttachment &drawBuffer = colorAttachments[colorAttachment];
328 
329             if (drawBuffer.isAttached() &&
330                 drawBufferStates[colorAttachment] != GL_NONE)
331             {
332                 RenderTargetD3D *drawRenderTarget = nullptr;
333                 ANGLE_TRY(drawBuffer.getRenderTarget(context, &drawRenderTarget));
334                 ASSERT(drawRenderTarget);
335 
336                 const bool invertColorSource   = UsePresentPathFast(mRenderer, readBuffer);
337                 gl::Rectangle actualSourceArea = sourceArea;
338                 if (invertColorSource)
339                 {
340                     RenderTarget11 *readRenderTarget11 = GetAs<RenderTarget11>(readRenderTarget);
341                     actualSourceArea.y                 = readRenderTarget11->getHeight() - sourceArea.y;
342                     actualSourceArea.height            = -sourceArea.height;
343                 }
344 
345                 const bool invertColorDest   = UsePresentPathFast(mRenderer, &drawBuffer);
346                 gl::Rectangle actualDestArea = destArea;
347                 if (invertColorDest)
348                 {
349                     RenderTarget11 *drawRenderTarget11 = GetAs<RenderTarget11>(drawRenderTarget);
350                     actualDestArea.y                   = drawRenderTarget11->getHeight() - destArea.y;
351                     actualDestArea.height              = -destArea.height;
352                 }
353 
354                 ANGLE_TRY(mRenderer->blitRenderbufferRect(
355                     context, actualSourceArea, actualDestArea, readRenderTarget, drawRenderTarget,
356                     filter, scissor, blitRenderTarget, false, false));
357             }
358         }
359     }
360 
361     if (blitDepth || blitStencil)
362     {
363         const gl::FramebufferAttachment *readBuffer = sourceFramebuffer->getDepthOrStencilbuffer();
364         ASSERT(readBuffer);
365 
366         RenderTargetD3D *readRenderTarget = nullptr;
367         ANGLE_TRY(readBuffer->getRenderTarget(context, &readRenderTarget));
368         ASSERT(readRenderTarget);
369 
370         const gl::FramebufferAttachment *drawBuffer = mState.getDepthOrStencilAttachment();
371         ASSERT(drawBuffer);
372 
373         RenderTargetD3D *drawRenderTarget = nullptr;
374         ANGLE_TRY(drawBuffer->getRenderTarget(context, &drawRenderTarget));
375         ASSERT(drawRenderTarget);
376 
377         ANGLE_TRY(mRenderer->blitRenderbufferRect(context, sourceArea, destArea, readRenderTarget,
378                                                   drawRenderTarget, filter, scissor, false,
379                                                   blitDepth, blitStencil));
380     }
381 
382     ANGLE_TRY(markAttachmentsDirty(context));
383     return gl::NoError();
384 }
385 
getRenderTargetImplementationFormat(RenderTargetD3D * renderTarget) const386 GLenum Framebuffer11::getRenderTargetImplementationFormat(RenderTargetD3D *renderTarget) const
387 {
388     RenderTarget11 *renderTarget11 = GetAs<RenderTarget11>(renderTarget);
389     return renderTarget11->getFormatSet().format().fboImplementationInternalFormat;
390 }
391 
updateColorRenderTarget(const gl::Context * context,size_t colorIndex)392 void Framebuffer11::updateColorRenderTarget(const gl::Context *context, size_t colorIndex)
393 {
394     UpdateCachedRenderTarget(context, mState.getColorAttachment(colorIndex),
395                              mCachedColorRenderTargets[colorIndex],
396                              &mColorRenderTargetsDirty[colorIndex]);
397 }
398 
updateDepthStencilRenderTarget(const gl::Context * context)399 void Framebuffer11::updateDepthStencilRenderTarget(const gl::Context *context)
400 {
401     UpdateCachedRenderTarget(context, mState.getDepthOrStencilAttachment(),
402                              mCachedDepthStencilRenderTarget, &mDepthStencilRenderTargetDirty);
403 }
404 
syncState(const gl::Context * context,const gl::Framebuffer::DirtyBits & dirtyBits)405 void Framebuffer11::syncState(const gl::Context *context,
406                               const gl::Framebuffer::DirtyBits &dirtyBits)
407 {
408     const auto &mergedDirtyBits = dirtyBits | mInternalDirtyBits;
409     mInternalDirtyBits.reset();
410 
411     for (auto dirtyBit : mergedDirtyBits)
412     {
413         switch (dirtyBit)
414         {
415             case gl::Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT:
416             case gl::Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT:
417                 updateDepthStencilRenderTarget(context);
418                 break;
419             case gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS:
420             case gl::Framebuffer::DIRTY_BIT_READ_BUFFER:
421                 break;
422             case gl::Framebuffer::DIRTY_BIT_DEFAULT_WIDTH:
423             case gl::Framebuffer::DIRTY_BIT_DEFAULT_HEIGHT:
424             case gl::Framebuffer::DIRTY_BIT_DEFAULT_SAMPLES:
425             case gl::Framebuffer::DIRTY_BIT_DEFAULT_FIXED_SAMPLE_LOCATIONS:
426                 break;
427             default:
428             {
429                 ASSERT(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0 &&
430                        dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX);
431                 size_t colorIndex =
432                     static_cast<size_t>(dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0);
433                 updateColorRenderTarget(context, colorIndex);
434                 break;
435             }
436         }
437     }
438 
439     // We should not have dirtied any additional state during our sync.
440     ASSERT(!mInternalDirtyBits.any());
441 
442     FramebufferD3D::syncState(context, dirtyBits);
443 
444     // Call this last to allow the state manager to take advantage of the cached render targets.
445     mRenderer->getStateManager()->invalidateRenderTarget();
446 
447     // Call this to syncViewport for framebuffer default parameters.
448     if (mState.getDefaultWidth() != 0 || mState.getDefaultHeight() != 0)
449     {
450         mRenderer->getStateManager()->invalidateViewport(context);
451     }
452 }
453 
signal(size_t channelID,const gl::Context * context)454 void Framebuffer11::signal(size_t channelID, const gl::Context *context)
455 {
456     if (channelID == gl::IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS)
457     {
458         // Stencil is redundant in this case.
459         mInternalDirtyBits.set(gl::Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT);
460         mCachedDepthStencilRenderTarget = nullptr;
461     }
462     else
463     {
464         mInternalDirtyBits.set(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 + channelID);
465         mCachedColorRenderTargets[channelID] = nullptr;
466     }
467 
468     // Notify the context we need to re-validate the RenderTarget.
469     // TODO(jmadill): Check that we're the active draw framebuffer.
470     mRenderer->getStateManager()->invalidateRenderTarget();
471 }
472 
getSamplePosition(size_t index,GLfloat * xy) const473 gl::Error Framebuffer11::getSamplePosition(size_t index, GLfloat *xy) const
474 {
475     const gl::FramebufferAttachment *attachment = mState.getFirstNonNullAttachment();
476     ASSERT(attachment);
477     GLsizei sampleCount = attachment->getSamples();
478 
479     d3d11_gl::GetSamplePosition(sampleCount, index, xy);
480     return gl::NoError();
481 }
482 
hasAnyInternalDirtyBit() const483 bool Framebuffer11::hasAnyInternalDirtyBit() const
484 {
485     return mInternalDirtyBits.any();
486 }
487 
syncInternalState(const gl::Context * context)488 void Framebuffer11::syncInternalState(const gl::Context *context)
489 {
490     syncState(context, gl::Framebuffer::DirtyBits());
491 }
492 
getFirstRenderTarget() const493 RenderTarget11 *Framebuffer11::getFirstRenderTarget() const
494 {
495     ASSERT(mInternalDirtyBits.none());
496     for (auto *renderTarget : mCachedColorRenderTargets)
497     {
498         if (renderTarget)
499         {
500             return renderTarget;
501         }
502     }
503 
504     return mCachedDepthStencilRenderTarget;
505 }
506 
507 }  // namespace rx
508