| // |
| // Copyright (c) 2014 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| // RendererD3D.cpp: Implementation of the base D3D Renderer. |
| |
| #include "libANGLE/renderer/d3d/RendererD3D.h" |
| |
| #include "common/MemoryBuffer.h" |
| #include "common/debug.h" |
| #include "common/utilities.h" |
| #include "libANGLE/Context.h" |
| #include "libANGLE/Display.h" |
| #include "libANGLE/Framebuffer.h" |
| #include "libANGLE/FramebufferAttachment.h" |
| #include "libANGLE/ImageIndex.h" |
| #include "libANGLE/ResourceManager.h" |
| #include "libANGLE/State.h" |
| #include "libANGLE/VertexArray.h" |
| #include "libANGLE/formatutils.h" |
| #include "libANGLE/renderer/ContextImpl.h" |
| #include "libANGLE/renderer/TextureImpl.h" |
| #include "libANGLE/renderer/d3d/BufferD3D.h" |
| #include "libANGLE/renderer/d3d/DeviceD3D.h" |
| #include "libANGLE/renderer/d3d/DisplayD3D.h" |
| #include "libANGLE/renderer/d3d/IndexDataManager.h" |
| #include "libANGLE/renderer/d3d/ProgramD3D.h" |
| #include "libANGLE/renderer/d3d/SamplerD3D.h" |
| #include "libANGLE/renderer/d3d/TextureD3D.h" |
| |
| namespace rx |
| { |
| |
| RendererD3D::RendererD3D(egl::Display *display) |
| : mDisplay(display), |
| mPresentPathFastEnabled(false), |
| mCapsInitialized(false), |
| mWorkaroundsInitialized(false), |
| mDisjoint(false), |
| mDeviceLost(false), |
| mWorkerThreadPool(4) |
| { |
| } |
| |
| RendererD3D::~RendererD3D() |
| { |
| cleanup(); |
| } |
| |
| void RendererD3D::cleanup() |
| { |
| for (auto &incompleteTexture : mIncompleteTextures) |
| { |
| incompleteTexture.second->onDestroy(mDisplay->getProxyContext()); |
| incompleteTexture.second.set(mDisplay->getProxyContext(), nullptr); |
| } |
| mIncompleteTextures.clear(); |
| } |
| |
| bool RendererD3D::skipDraw(const gl::ContextState &data, GLenum drawMode) |
| { |
| const gl::State &state = data.getState(); |
| |
| if (drawMode == GL_POINTS) |
| { |
| bool usesPointSize = GetImplAs<ProgramD3D>(state.getProgram())->usesPointSize(); |
| |
| // ProgramBinary assumes non-point rendering if gl_PointSize isn't written, |
| // which affects varying interpolation. Since the value of gl_PointSize is |
| // undefined when not written, just skip drawing to avoid unexpected results. |
| if (!usesPointSize && !state.isTransformFeedbackActiveUnpaused()) |
| { |
| // Notify developers of risking undefined behavior. |
| WARN() << "Point rendering without writing to gl_PointSize."; |
| return true; |
| } |
| } |
| else if (gl::IsTriangleMode(drawMode)) |
| { |
| if (state.getRasterizerState().cullFace && |
| state.getRasterizerState().cullMode == GL_FRONT_AND_BACK) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| gl::Error RendererD3D::markTransformFeedbackUsage(const gl::ContextState &data) |
| { |
| const gl::TransformFeedback *transformFeedback = data.getState().getCurrentTransformFeedback(); |
| for (size_t i = 0; i < transformFeedback->getIndexedBufferCount(); i++) |
| { |
| const gl::OffsetBindingPointer<gl::Buffer> &binding = |
| transformFeedback->getIndexedBuffer(i); |
| if (binding.get() != nullptr) |
| { |
| BufferD3D *bufferD3D = GetImplAs<BufferD3D>(binding.get()); |
| ANGLE_TRY(bufferD3D->markTransformFeedbackUsage()); |
| } |
| } |
| |
| return gl::NoError(); |
| } |
| |
| size_t RendererD3D::getBoundFramebufferTextures(const gl::ContextState &data, |
| FramebufferTextureArray *outTextureArray) |
| { |
| size_t textureCount = 0; |
| |
| const gl::Framebuffer *drawFramebuffer = data.getState().getDrawFramebuffer(); |
| for (size_t i = 0; i < drawFramebuffer->getNumColorBuffers(); i++) |
| { |
| const gl::FramebufferAttachment *attachment = drawFramebuffer->getColorbuffer(i); |
| if (attachment && attachment->type() == GL_TEXTURE) |
| { |
| (*outTextureArray)[textureCount++] = attachment->getTexture(); |
| } |
| } |
| |
| const gl::FramebufferAttachment *depthStencilAttachment = |
| drawFramebuffer->getDepthOrStencilbuffer(); |
| if (depthStencilAttachment && depthStencilAttachment->type() == GL_TEXTURE) |
| { |
| (*outTextureArray)[textureCount++] = depthStencilAttachment->getTexture(); |
| } |
| |
| std::sort(outTextureArray->begin(), outTextureArray->begin() + textureCount); |
| |
| return textureCount; |
| } |
| |
| gl::Texture *RendererD3D::getIncompleteTexture(const gl::Context *context, GLenum type) |
| { |
| if (mIncompleteTextures.find(type) == mIncompleteTextures.end()) |
| { |
| GLImplFactory *implFactory = context->getImplementation(); |
| const GLubyte color[] = {0, 0, 0, 255}; |
| const gl::Extents colorSize(1, 1, 1); |
| const gl::PixelUnpackState unpack(1, 0); |
| const gl::Box area(0, 0, 0, 1, 1, 1); |
| |
| // If a texture is external use a 2D texture for the incomplete texture |
| GLenum createType = (type == GL_TEXTURE_EXTERNAL_OES) ? GL_TEXTURE_2D : type; |
| |
| // Skip the API layer to avoid needing to pass the Context and mess with dirty bits. |
| gl::Texture *t = |
| new gl::Texture(implFactory, std::numeric_limits<GLuint>::max(), createType); |
| if (createType == GL_TEXTURE_2D_MULTISAMPLE) |
| { |
| t->setStorageMultisample(nullptr, createType, 1, GL_RGBA8, colorSize, true); |
| } |
| else |
| { |
| t->setStorage(nullptr, createType, 1, GL_RGBA8, colorSize); |
| } |
| if (type == GL_TEXTURE_CUBE_MAP) |
| { |
| for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; |
| face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; face++) |
| { |
| t->getImplementation()->setSubImage(nullptr, face, 0, area, GL_RGBA8, |
| GL_UNSIGNED_BYTE, unpack, color); |
| } |
| } |
| else if (type == GL_TEXTURE_2D_MULTISAMPLE) |
| { |
| gl::ColorF clearValue(0, 0, 0, 1); |
| gl::ImageIndex index = gl::ImageIndex::Make2DMultisample(); |
| GetImplAs<TextureD3D>(t)->clearLevel(context, index, clearValue); |
| } |
| else |
| { |
| t->getImplementation()->setSubImage(nullptr, createType, 0, area, GL_RGBA8, |
| GL_UNSIGNED_BYTE, unpack, color); |
| } |
| mIncompleteTextures[type].set(context, t); |
| } |
| |
| return mIncompleteTextures[type].get(); |
| } |
| |
| GLenum RendererD3D::getResetStatus() |
| { |
| if (!mDeviceLost) |
| { |
| if (testDeviceLost()) |
| { |
| mDeviceLost = true; |
| notifyDeviceLost(); |
| return GL_UNKNOWN_CONTEXT_RESET_EXT; |
| } |
| return GL_NO_ERROR; |
| } |
| |
| if (testDeviceResettable()) |
| { |
| return GL_NO_ERROR; |
| } |
| |
| return GL_UNKNOWN_CONTEXT_RESET_EXT; |
| } |
| |
| void RendererD3D::notifyDeviceLost() |
| { |
| mDisplay->notifyDeviceLost(); |
| } |
| |
| std::string RendererD3D::getVendorString() const |
| { |
| LUID adapterLuid = {0}; |
| |
| if (getLUID(&adapterLuid)) |
| { |
| char adapterLuidString[64]; |
| sprintf_s(adapterLuidString, sizeof(adapterLuidString), "(adapter LUID: %08x%08x)", |
| adapterLuid.HighPart, adapterLuid.LowPart); |
| return std::string(adapterLuidString); |
| } |
| |
| return std::string(""); |
| } |
| |
| void RendererD3D::setGPUDisjoint() |
| { |
| mDisjoint = true; |
| } |
| |
| GLint RendererD3D::getGPUDisjoint() |
| { |
| bool disjoint = mDisjoint; |
| |
| // Disjoint flag is cleared when read |
| mDisjoint = false; |
| |
| return disjoint; |
| } |
| |
| GLint64 RendererD3D::getTimestamp() |
| { |
| // D3D has no way to get an actual timestamp reliably so 0 is returned |
| return 0; |
| } |
| |
| void RendererD3D::ensureCapsInitialized() const |
| { |
| if (!mCapsInitialized) |
| { |
| generateCaps(&mNativeCaps, &mNativeTextureCaps, &mNativeExtensions, &mNativeLimitations); |
| mCapsInitialized = true; |
| } |
| } |
| |
| const gl::Caps &RendererD3D::getNativeCaps() const |
| { |
| ensureCapsInitialized(); |
| return mNativeCaps; |
| } |
| |
| const gl::TextureCapsMap &RendererD3D::getNativeTextureCaps() const |
| { |
| ensureCapsInitialized(); |
| return mNativeTextureCaps; |
| } |
| |
| const gl::Extensions &RendererD3D::getNativeExtensions() const |
| { |
| ensureCapsInitialized(); |
| return mNativeExtensions; |
| } |
| |
| const gl::Limitations &RendererD3D::getNativeLimitations() const |
| { |
| ensureCapsInitialized(); |
| return mNativeLimitations; |
| } |
| |
| angle::WorkerThreadPool *RendererD3D::getWorkerThreadPool() |
| { |
| return &mWorkerThreadPool; |
| } |
| |
| bool RendererD3D::isRobustResourceInitEnabled() const |
| { |
| return mDisplay->isRobustResourceInitEnabled(); |
| } |
| |
| unsigned int GetBlendSampleMask(const gl::ContextState &data, int samples) |
| { |
| const auto &glState = data.getState(); |
| unsigned int mask = 0; |
| if (glState.isSampleCoverageEnabled()) |
| { |
| GLfloat coverageValue = glState.getSampleCoverageValue(); |
| if (coverageValue != 0) |
| { |
| float threshold = 0.5f; |
| |
| for (int i = 0; i < samples; ++i) |
| { |
| mask <<= 1; |
| |
| if ((i + 1) * coverageValue >= threshold) |
| { |
| threshold += 1.0f; |
| mask |= 1; |
| } |
| } |
| } |
| |
| bool coverageInvert = glState.getSampleCoverageInvert(); |
| if (coverageInvert) |
| { |
| mask = ~mask; |
| } |
| } |
| else |
| { |
| mask = 0xFFFFFFFF; |
| } |
| |
| return mask; |
| } |
| |
| } // namespace rx |