blob: e0cb86d287616ad3edca222c4ddaf016a36b844f [file] [log] [blame]
//
// 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/Display.h"
#include "libANGLE/Framebuffer.h"
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/ResourceManager.h"
#include "libANGLE/State.h"
#include "libANGLE/VertexArray.h"
#include "libANGLE/formatutils.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"
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.set(nullptr);
}
mIncompleteTextures.clear();
}
unsigned int RendererD3D::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;
}
// For each Direct3D sampler of either the pixel or vertex stage,
// looks up the corresponding OpenGL texture image unit and texture type,
// and sets the texture and its addressing/filtering state (or NULL when inactive).
// Sampler mapping needs to be up-to-date on the program object before this is called.
gl::Error RendererD3D::applyTextures(GLImplFactory *implFactory,
const gl::ContextState &data,
gl::SamplerType shaderType,
const FramebufferTextureArray &framebufferTextures,
size_t framebufferTextureCount)
{
const auto &glState = data.getState();
const auto &caps = data.getCaps();
ProgramD3D *programD3D = GetImplAs<ProgramD3D>(glState.getProgram());
ASSERT(!programD3D->isSamplerMappingDirty());
// TODO(jmadill): Use the Program's sampler bindings.
unsigned int samplerRange = programD3D->getUsedSamplerRange(shaderType);
for (unsigned int samplerIndex = 0; samplerIndex < samplerRange; samplerIndex++)
{
GLenum textureType = programD3D->getSamplerTextureType(shaderType, samplerIndex);
GLint textureUnit = programD3D->getSamplerMapping(shaderType, samplerIndex, caps);
if (textureUnit != -1)
{
gl::Texture *texture = glState.getSamplerTexture(textureUnit, textureType);
ASSERT(texture);
gl::Sampler *samplerObject = glState.getSampler(textureUnit);
const gl::SamplerState &samplerState =
samplerObject ? samplerObject->getSamplerState() : texture->getSamplerState();
// TODO: std::binary_search may become unavailable using older versions of GCC
if (texture->getTextureState().isSamplerComplete(samplerState, data) &&
!std::binary_search(framebufferTextures.begin(),
framebufferTextures.begin() + framebufferTextureCount, texture))
{
ANGLE_TRY(setSamplerState(shaderType, samplerIndex, texture, samplerState));
ANGLE_TRY(setTexture(shaderType, samplerIndex, texture));
}
else
{
// Texture is not sampler complete or it is in use by the framebuffer. Bind the
// incomplete texture.
gl::Texture *incompleteTexture = getIncompleteTexture(implFactory, textureType);
ANGLE_TRY(setSamplerState(shaderType, samplerIndex, incompleteTexture,
incompleteTexture->getSamplerState()));
ANGLE_TRY(setTexture(shaderType, samplerIndex, incompleteTexture));
}
}
else
{
// No texture bound to this slot even though it is used by the shader, bind a NULL
// texture
ANGLE_TRY(setTexture(shaderType, samplerIndex, nullptr));
}
}
// Set all the remaining textures to NULL
size_t samplerCount = (shaderType == gl::SAMPLER_PIXEL) ? caps.maxTextureImageUnits
: caps.maxVertexTextureImageUnits;
clearTextures(shaderType, samplerRange, samplerCount);
return gl::NoError();
}
gl::Error RendererD3D::applyTextures(GLImplFactory *implFactory, const gl::ContextState &data)
{
FramebufferTextureArray framebufferTextures;
size_t framebufferSerialCount = getBoundFramebufferTextures(data, &framebufferTextures);
ANGLE_TRY(applyTextures(implFactory, data, gl::SAMPLER_VERTEX, framebufferTextures,
framebufferSerialCount));
ANGLE_TRY(applyTextures(implFactory, data, gl::SAMPLER_PIXEL, framebufferTextures,
framebufferSerialCount));
return gl::NoError();
}
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 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(GLImplFactory *implFactory, GLenum type)
{
if (mIncompleteTextures.find(type) == mIncompleteTextures.end())
{
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);
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
{
t->getImplementation()->setSubImage(nullptr, createType, 0, area, GL_RGBA8,
GL_UNSIGNED_BYTE, unpack, color);
}
mIncompleteTextures[type].set(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;
}
} // namespace rx