| // |
| // Copyright 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. |
| // |
| |
| // ProgramD3D.cpp: Defines the rx::ProgramD3D class which implements rx::ProgramImpl. |
| |
| #include "libANGLE/renderer/d3d/ProgramD3D.h" |
| |
| #include "common/MemoryBuffer.h" |
| #include "common/bitset_utils.h" |
| #include "common/string_utils.h" |
| #include "common/utilities.h" |
| #include "libANGLE/Context.h" |
| #include "libANGLE/Program.h" |
| #include "libANGLE/ProgramLinkedResources.h" |
| #include "libANGLE/Uniform.h" |
| #include "libANGLE/VertexArray.h" |
| #include "libANGLE/features.h" |
| #include "libANGLE/queryconversions.h" |
| #include "libANGLE/renderer/ContextImpl.h" |
| #include "libANGLE/renderer/d3d/ContextD3D.h" |
| #include "libANGLE/renderer/d3d/ShaderD3D.h" |
| #include "libANGLE/renderer/d3d/ShaderExecutableD3D.h" |
| #include "libANGLE/renderer/d3d/VertexDataManager.h" |
| #include "libANGLE/renderer/renderer_utils.h" |
| #include "libANGLE/trace.h" |
| |
| using namespace angle; |
| |
| namespace rx |
| { |
| namespace |
| { |
| bool HasFlatInterpolationVarying(const std::vector<sh::ShaderVariable> &varyings) |
| { |
| // Note: this assumes nested structs can only be packed with one interpolation. |
| for (const auto &varying : varyings) |
| { |
| if (varying.interpolation == sh::INTERPOLATION_FLAT) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool FindFlatInterpolationVaryingPerShader(const gl::SharedCompiledShaderState &shader) |
| { |
| ASSERT(shader); |
| switch (shader->shaderType) |
| { |
| case gl::ShaderType::Vertex: |
| return HasFlatInterpolationVarying(shader->outputVaryings); |
| case gl::ShaderType::Fragment: |
| return HasFlatInterpolationVarying(shader->inputVaryings); |
| case gl::ShaderType::Geometry: |
| return HasFlatInterpolationVarying(shader->inputVaryings) || |
| HasFlatInterpolationVarying(shader->outputVaryings); |
| default: |
| UNREACHABLE(); |
| return false; |
| } |
| } |
| |
| bool FindFlatInterpolationVarying(const gl::ShaderMap<gl::SharedCompiledShaderState> &shaders) |
| { |
| for (gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes) |
| { |
| const gl::SharedCompiledShaderState &shader = shaders[shaderType]; |
| if (!shader) |
| { |
| continue; |
| } |
| |
| if (FindFlatInterpolationVaryingPerShader(shader)) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| class HLSLBlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFactory |
| { |
| public: |
| sh::BlockLayoutEncoder *makeEncoder() override |
| { |
| return new sh::HLSLBlockEncoder(sh::HLSLBlockEncoder::ENCODE_PACKED, false); |
| } |
| }; |
| |
| // GetExecutableTask class |
| class GetExecutableTask : public LinkSubTask, public d3d::Context |
| { |
| public: |
| GetExecutableTask(ProgramD3D *program, const SharedCompiledShaderStateD3D &shader) |
| : mProgram(program), mExecutable(program->getExecutable()), mShader(shader) |
| {} |
| ~GetExecutableTask() override = default; |
| |
| angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override |
| { |
| ASSERT((mResult == angle::Result::Continue) == (mStoredHR == S_OK)); |
| |
| ANGLE_TRY(checkTask(context, infoLog)); |
| |
| // Append debug info |
| if (mShader && mShaderExecutable != nullptr) |
| { |
| mShader->appendDebugInfo(mShaderExecutable->getDebugInfo()); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| void handleResult(HRESULT hr, |
| const char *message, |
| const char *file, |
| const char *function, |
| unsigned int line) override |
| { |
| mStoredHR = hr; |
| mStoredMessage = message; |
| mStoredFile = file; |
| mStoredFunction = function; |
| mStoredLine = line; |
| } |
| |
| protected: |
| void popError(d3d::Context *context) |
| { |
| ASSERT(mStoredFile); |
| ASSERT(mStoredFunction); |
| context->handleResult(mStoredHR, mStoredMessage.c_str(), mStoredFile, mStoredFunction, |
| mStoredLine); |
| } |
| |
| angle::Result checkTask(const gl::Context *context, gl::InfoLog &infoLog) |
| { |
| // Forward any logs |
| if (!mInfoLog.empty()) |
| { |
| infoLog << mInfoLog.str(); |
| } |
| |
| // Forward any errors |
| if (mResult != angle::Result::Continue) |
| { |
| ContextD3D *contextD3D = GetImplAs<ContextD3D>(context); |
| popError(contextD3D); |
| return angle::Result::Stop; |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| ProgramD3D *mProgram = nullptr; |
| ProgramExecutableD3D *mExecutable = nullptr; |
| angle::Result mResult = angle::Result::Continue; |
| gl::InfoLog mInfoLog; |
| ShaderExecutableD3D *mShaderExecutable = nullptr; |
| SharedCompiledShaderStateD3D mShader; |
| |
| // Error handling |
| HRESULT mStoredHR = S_OK; |
| std::string mStoredMessage; |
| const char *mStoredFile = nullptr; |
| const char *mStoredFunction = nullptr; |
| unsigned int mStoredLine = 0; |
| }; |
| } // anonymous namespace |
| |
| // ProgramD3DMetadata Implementation |
| ProgramD3DMetadata::ProgramD3DMetadata( |
| RendererD3D *renderer, |
| const gl::SharedCompiledShaderState &fragmentShader, |
| const gl::ShaderMap<SharedCompiledShaderStateD3D> &attachedShaders, |
| EGLenum clientType, |
| int shaderVersion) |
| : mRendererMajorShaderModel(renderer->getMajorShaderModel()), |
| mShaderModelSuffix(renderer->getShaderModelSuffix()), |
| mUsesInstancedPointSpriteEmulation( |
| renderer->getFeatures().useInstancedPointSpriteEmulation.enabled), |
| mUsesViewScale(renderer->presentPathFastEnabled()), |
| mCanSelectViewInVertexShader(renderer->canSelectViewInVertexShader()), |
| mFragmentShader(fragmentShader), |
| mAttachedShaders(attachedShaders), |
| mClientType(clientType), |
| mShaderVersion(shaderVersion) |
| {} |
| |
| ProgramD3DMetadata::~ProgramD3DMetadata() = default; |
| |
| int ProgramD3DMetadata::getRendererMajorShaderModel() const |
| { |
| return mRendererMajorShaderModel; |
| } |
| |
| bool ProgramD3DMetadata::usesBroadcast(const gl::Version &clientVersion) const |
| { |
| const SharedCompiledShaderStateD3D &shader = mAttachedShaders[gl::ShaderType::Fragment]; |
| return shader && shader->usesFragColor && shader->usesMultipleRenderTargets && |
| clientVersion.major < 3; |
| } |
| |
| bool ProgramD3DMetadata::usesSecondaryColor() const |
| { |
| const SharedCompiledShaderStateD3D &shader = mAttachedShaders[gl::ShaderType::Fragment]; |
| return shader && shader->usesSecondaryColor; |
| } |
| |
| FragDepthUsage ProgramD3DMetadata::getFragDepthUsage() const |
| { |
| const SharedCompiledShaderStateD3D &shader = mAttachedShaders[gl::ShaderType::Fragment]; |
| return shader ? shader->fragDepthUsage : FragDepthUsage::Unused; |
| } |
| |
| bool ProgramD3DMetadata::usesPointCoord() const |
| { |
| const SharedCompiledShaderStateD3D &shader = mAttachedShaders[gl::ShaderType::Fragment]; |
| return shader && shader->usesPointCoord; |
| } |
| |
| bool ProgramD3DMetadata::usesFragCoord() const |
| { |
| const SharedCompiledShaderStateD3D &shader = mAttachedShaders[gl::ShaderType::Fragment]; |
| return shader && shader->usesFragCoord; |
| } |
| |
| bool ProgramD3DMetadata::usesPointSize() const |
| { |
| const SharedCompiledShaderStateD3D &shader = mAttachedShaders[gl::ShaderType::Vertex]; |
| return shader && shader->usesPointSize; |
| } |
| |
| bool ProgramD3DMetadata::usesInsertedPointCoordValue() const |
| { |
| return (!usesPointSize() || !mUsesInstancedPointSpriteEmulation) && usesPointCoord() && |
| mRendererMajorShaderModel >= 4; |
| } |
| |
| bool ProgramD3DMetadata::usesViewScale() const |
| { |
| return mUsesViewScale; |
| } |
| |
| bool ProgramD3DMetadata::hasMultiviewEnabled() const |
| { |
| const SharedCompiledShaderStateD3D &shader = mAttachedShaders[gl::ShaderType::Vertex]; |
| return shader && shader->hasMultiviewEnabled; |
| } |
| |
| bool ProgramD3DMetadata::usesVertexID() const |
| { |
| const SharedCompiledShaderStateD3D &shader = mAttachedShaders[gl::ShaderType::Vertex]; |
| return shader && shader->usesVertexID; |
| } |
| |
| bool ProgramD3DMetadata::usesViewID() const |
| { |
| const SharedCompiledShaderStateD3D &shader = mAttachedShaders[gl::ShaderType::Fragment]; |
| return shader && shader->usesViewID; |
| } |
| |
| bool ProgramD3DMetadata::canSelectViewInVertexShader() const |
| { |
| return mCanSelectViewInVertexShader; |
| } |
| |
| bool ProgramD3DMetadata::addsPointCoordToVertexShader() const |
| { |
| // PointSprite emulation requiress that gl_PointCoord is present in the vertex shader |
| // VS_OUTPUT structure to ensure compatibility with the generated PS_INPUT of the pixel shader. |
| // Even with a geometry shader, the app can render triangles or lines and reference |
| // gl_PointCoord in the fragment shader, requiring us to provide a placeholder value. For |
| // simplicity, we always add this to the vertex shader when the fragment shader |
| // references gl_PointCoord, even if we could skip it in the geometry shader. |
| return (mUsesInstancedPointSpriteEmulation && usesPointCoord()) || |
| usesInsertedPointCoordValue(); |
| } |
| |
| bool ProgramD3DMetadata::usesTransformFeedbackGLPosition() const |
| { |
| // gl_Position only needs to be outputted from the vertex shader if transform feedback is |
| // active. This isn't supported on D3D11 Feature Level 9_3, so we don't output gl_Position from |
| // the vertex shader in this case. This saves us 1 output vector. |
| return !(mRendererMajorShaderModel >= 4 && mShaderModelSuffix != ""); |
| } |
| |
| bool ProgramD3DMetadata::usesSystemValuePointSize() const |
| { |
| return !mUsesInstancedPointSpriteEmulation && usesPointSize(); |
| } |
| |
| bool ProgramD3DMetadata::usesMultipleFragmentOuts() const |
| { |
| const SharedCompiledShaderStateD3D &shader = mAttachedShaders[gl::ShaderType::Fragment]; |
| return shader && shader->usesMultipleRenderTargets; |
| } |
| |
| bool ProgramD3DMetadata::usesCustomOutVars() const |
| { |
| switch (mClientType) |
| { |
| case EGL_OPENGL_API: |
| return mShaderVersion >= 130; |
| default: |
| return mShaderVersion >= 300; |
| } |
| } |
| |
| bool ProgramD3DMetadata::usesSampleMask() const |
| { |
| const SharedCompiledShaderStateD3D &shader = mAttachedShaders[gl::ShaderType::Fragment]; |
| return shader && shader->usesSampleMask; |
| } |
| |
| const gl::SharedCompiledShaderState &ProgramD3DMetadata::getFragmentShader() const |
| { |
| return mFragmentShader; |
| } |
| |
| uint8_t ProgramD3DMetadata::getClipDistanceArraySize() const |
| { |
| const SharedCompiledShaderStateD3D &shader = mAttachedShaders[gl::ShaderType::Vertex]; |
| return shader ? shader->clipDistanceSize : 0; |
| } |
| |
| uint8_t ProgramD3DMetadata::getCullDistanceArraySize() const |
| { |
| const SharedCompiledShaderStateD3D &shader = mAttachedShaders[gl::ShaderType::Vertex]; |
| return shader ? shader->cullDistanceSize : 0; |
| } |
| |
| // ProgramD3D Implementation |
| |
| class ProgramD3D::GetVertexExecutableTask : public GetExecutableTask |
| { |
| public: |
| GetVertexExecutableTask(ProgramD3D *program, const SharedCompiledShaderStateD3D &shader) |
| : GetExecutableTask(program, shader) |
| {} |
| ~GetVertexExecutableTask() override = default; |
| |
| void operator()() override |
| { |
| ANGLE_TRACE_EVENT0("gpu.angle", "GetVertexExecutableTask::run"); |
| |
| mExecutable->updateCachedImage2DBindLayoutFromShader(gl::ShaderType::Vertex); |
| mResult = mExecutable->getVertexExecutableForCachedInputLayout( |
| this, mProgram->mRenderer, &mShaderExecutable, &mInfoLog); |
| } |
| }; |
| |
| class ProgramD3D::GetPixelExecutableTask : public GetExecutableTask |
| { |
| public: |
| GetPixelExecutableTask(ProgramD3D *program, const SharedCompiledShaderStateD3D &shader) |
| : GetExecutableTask(program, shader) |
| {} |
| ~GetPixelExecutableTask() override = default; |
| |
| void operator()() override |
| { |
| ANGLE_TRACE_EVENT0("gpu.angle", "GetPixelExecutableTask::run"); |
| if (!mShader) |
| { |
| return; |
| } |
| |
| mExecutable->updateCachedOutputLayoutFromShader(); |
| mExecutable->updateCachedImage2DBindLayoutFromShader(gl::ShaderType::Fragment); |
| mResult = mExecutable->getPixelExecutableForCachedOutputLayout( |
| this, mProgram->mRenderer, &mShaderExecutable, &mInfoLog); |
| } |
| }; |
| |
| class ProgramD3D::GetGeometryExecutableTask : public GetExecutableTask |
| { |
| public: |
| GetGeometryExecutableTask(ProgramD3D *program, |
| const SharedCompiledShaderStateD3D &shader, |
| const gl::Caps &caps, |
| gl::ProvokingVertexConvention provokingVertex) |
| : GetExecutableTask(program, shader), mCaps(caps), mProvokingVertex(provokingVertex) |
| {} |
| ~GetGeometryExecutableTask() override = default; |
| |
| void operator()() override |
| { |
| ANGLE_TRACE_EVENT0("gpu.angle", "GetGeometryExecutableTask::run"); |
| // Auto-generate the geometry shader here, if we expect to be using point rendering in |
| // D3D11. |
| if (mExecutable->usesGeometryShader(mProgram->mRenderer, mProvokingVertex, |
| gl::PrimitiveMode::Points)) |
| { |
| mResult = mExecutable->getGeometryExecutableForPrimitiveType( |
| this, mProgram->mRenderer, mCaps, mProvokingVertex, gl::PrimitiveMode::Points, |
| &mShaderExecutable, &mInfoLog); |
| } |
| } |
| |
| private: |
| const gl::Caps &mCaps; |
| gl::ProvokingVertexConvention mProvokingVertex; |
| }; |
| |
| class ProgramD3D::GetComputeExecutableTask : public GetExecutableTask |
| { |
| public: |
| GetComputeExecutableTask(ProgramD3D *program, const SharedCompiledShaderStateD3D &shader) |
| : GetExecutableTask(program, shader) |
| {} |
| ~GetComputeExecutableTask() override = default; |
| |
| void operator()() override |
| { |
| ANGLE_TRACE_EVENT0("gpu.angle", "GetComputeExecutableTask::run"); |
| |
| mExecutable->updateCachedImage2DBindLayoutFromShader(gl::ShaderType::Compute); |
| mResult = mExecutable->getComputeExecutableForImage2DBindLayout( |
| this, mProgram->mRenderer, &mShaderExecutable, &mInfoLog); |
| } |
| }; |
| |
| class ProgramD3D::LinkLoadTaskD3D : public d3d::Context, public LinkTask |
| { |
| public: |
| LinkLoadTaskD3D(ProgramD3D *program) : mProgram(program), mExecutable(program->getExecutable()) |
| { |
| ASSERT(mProgram); |
| } |
| ~LinkLoadTaskD3D() override = default; |
| |
| angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override |
| { |
| if (mStoredHR != S_OK) |
| { |
| GetImplAs<ContextD3D>(context)->handleResult(mStoredHR, mStoredMessage.c_str(), |
| mStoredFile, mStoredFunction, mStoredLine); |
| return angle::Result::Stop; |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| void handleResult(HRESULT hr, |
| const char *message, |
| const char *file, |
| const char *function, |
| unsigned int line) override |
| { |
| mStoredHR = hr; |
| mStoredMessage = message; |
| mStoredFile = file; |
| mStoredFunction = function; |
| mStoredLine = line; |
| } |
| |
| protected: |
| // The front-end ensures that the program is not accessed while linking, so it is safe to |
| // direclty access the state from a potentially parallel job. |
| ProgramD3D *mProgram; |
| ProgramExecutableD3D *mExecutable = nullptr; |
| |
| // Error handling |
| HRESULT mStoredHR = S_OK; |
| std::string mStoredMessage; |
| const char *mStoredFile = nullptr; |
| const char *mStoredFunction = nullptr; |
| unsigned int mStoredLine = 0; |
| }; |
| |
| class ProgramD3D::LinkTaskD3D final : public LinkLoadTaskD3D |
| { |
| public: |
| LinkTaskD3D(const gl::Version &clientVersion, |
| const gl::Caps &caps, |
| EGLenum clientType, |
| ProgramD3D *program, |
| gl::ProvokingVertexConvention provokingVertex) |
| : LinkLoadTaskD3D(program), |
| mClientVersion(clientVersion), |
| mCaps(caps), |
| mClientType(clientType), |
| mProvokingVertex(provokingVertex) |
| {} |
| ~LinkTaskD3D() override = default; |
| |
| std::vector<std::shared_ptr<LinkSubTask>> link(const gl::ProgramLinkedResources &resources, |
| const gl::ProgramMergedVaryings &mergedVaryings, |
| bool *areSubTasksOptionalOut) override; |
| |
| private: |
| const gl::Version mClientVersion; |
| const gl::Caps &mCaps; |
| const EGLenum mClientType; |
| const gl::ProvokingVertexConvention mProvokingVertex; |
| }; |
| |
| std::vector<std::shared_ptr<LinkSubTask>> ProgramD3D::LinkTaskD3D::link( |
| const gl::ProgramLinkedResources &resources, |
| const gl::ProgramMergedVaryings &mergedVaryings, |
| bool *areSubTasksOptionalOut) |
| { |
| ANGLE_TRACE_EVENT0("gpu.angle", "LinkTaskD3D::link"); |
| |
| angle::Result result = |
| mProgram->linkJobImpl(this, mCaps, mClientVersion, mClientType, resources, mergedVaryings); |
| ASSERT((result == angle::Result::Continue) == (mStoredHR == S_OK)); |
| |
| if (result != angle::Result::Continue) |
| { |
| return {}; |
| } |
| |
| // Create the subtasks |
| std::vector<std::shared_ptr<LinkSubTask>> subTasks; |
| |
| if (mExecutable->hasShaderStage(gl::ShaderType::Compute)) |
| { |
| subTasks.push_back(std::make_shared<GetComputeExecutableTask>( |
| mProgram, mProgram->getAttachedShader(gl::ShaderType::Compute))); |
| } |
| else |
| { |
| // Geometry shaders are currently only used internally, so there is no corresponding shader |
| // object at the interface level. For now the geometry shader debug info is prepended to the |
| // vertex shader. |
| subTasks.push_back(std::make_shared<GetVertexExecutableTask>( |
| mProgram, mProgram->getAttachedShader(gl::ShaderType::Vertex))); |
| subTasks.push_back(std::make_shared<GetPixelExecutableTask>( |
| mProgram, mProgram->getAttachedShader(gl::ShaderType::Fragment))); |
| subTasks.push_back(std::make_shared<GetGeometryExecutableTask>( |
| mProgram, mProgram->getAttachedShader(gl::ShaderType::Vertex), mCaps, |
| mProvokingVertex)); |
| } |
| |
| *areSubTasksOptionalOut = false; |
| return subTasks; |
| } |
| |
| class ProgramD3D::LoadTaskD3D final : public LinkLoadTaskD3D |
| { |
| public: |
| LoadTaskD3D(ProgramD3D *program, angle::MemoryBuffer &&streamData) |
| : LinkLoadTaskD3D(program), mStreamData(std::move(streamData)) |
| {} |
| ~LoadTaskD3D() override = default; |
| |
| std::vector<std::shared_ptr<LinkSubTask>> load(bool *areSubTasksOptionalOut) override |
| { |
| ANGLE_TRACE_EVENT0("gpu.angle", "LoadTaskD3D::load"); |
| |
| gl::BinaryInputStream stream(mStreamData.data(), mStreamData.size()); |
| mResult = mExecutable->loadBinaryShaderExecutables(this, mProgram->mRenderer, &stream); |
| |
| return {}; |
| } |
| |
| angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override |
| { |
| ANGLE_TRY(LinkLoadTaskD3D::getResult(context, infoLog)); |
| return mResult; |
| } |
| |
| private: |
| angle::MemoryBuffer mStreamData; |
| angle::Result mResult = angle::Result::Continue; |
| }; |
| |
| ProgramD3D::ProgramD3D(const gl::ProgramState &state, RendererD3D *renderer) |
| : ProgramImpl(state), mRenderer(renderer) |
| {} |
| |
| ProgramD3D::~ProgramD3D() = default; |
| |
| void ProgramD3D::destroy(const gl::Context *context) |
| { |
| getExecutable()->reset(); |
| } |
| |
| angle::Result ProgramD3D::load(const gl::Context *context, |
| gl::BinaryInputStream *stream, |
| std::shared_ptr<LinkTask> *loadTaskOut, |
| egl::CacheGetResult *resultOut) |
| { |
| if (!getExecutable()->load(context, mRenderer, stream)) |
| { |
| mState.getExecutable().getInfoLog() |
| << "Invalid program binary, device configuration has changed."; |
| return angle::Result::Continue; |
| } |
| |
| // Copy the remaining data from the stream locally so that the client can't modify it when |
| // loading off thread. |
| angle::MemoryBuffer streamData; |
| const size_t dataSize = stream->remainingSize(); |
| if (!streamData.resize(dataSize)) |
| { |
| mState.getExecutable().getInfoLog() |
| << "Failed to copy program binary data to local buffer."; |
| return angle::Result::Stop; |
| } |
| memcpy(streamData.data(), stream->data() + stream->offset(), dataSize); |
| |
| // Note: pretty much all the above can also be moved to the task |
| *loadTaskOut = std::shared_ptr<LinkTask>(new LoadTaskD3D(this, std::move(streamData))); |
| *resultOut = egl::CacheGetResult::GetSuccess; |
| |
| return angle::Result::Continue; |
| } |
| |
| void ProgramD3D::save(const gl::Context *context, gl::BinaryOutputStream *stream) |
| { |
| getExecutable()->save(context, mRenderer, stream); |
| } |
| |
| void ProgramD3D::setBinaryRetrievableHint(bool /* retrievable */) {} |
| |
| void ProgramD3D::setSeparable(bool /* separable */) {} |
| |
| void ProgramD3D::prepareForLink(const gl::ShaderMap<ShaderImpl *> &shaders) |
| { |
| ProgramExecutableD3D *executableD3D = getExecutable(); |
| |
| for (gl::ShaderType shaderType : gl::AllShaderTypes()) |
| { |
| executableD3D->mAttachedShaders[shaderType].reset(); |
| |
| if (shaders[shaderType] != nullptr) |
| { |
| const ShaderD3D *shaderD3D = GetAs<ShaderD3D>(shaders[shaderType]); |
| executableD3D->mAttachedShaders[shaderType] = shaderD3D->getCompiledState(); |
| } |
| } |
| } |
| |
| angle::Result ProgramD3D::link(const gl::Context *context, std::shared_ptr<LinkTask> *linkTaskOut) |
| { |
| ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::link"); |
| const gl::Version &clientVersion = context->getClientVersion(); |
| const gl::Caps &caps = context->getCaps(); |
| EGLenum clientType = context->getClientType(); |
| |
| // Ensure the compiler is initialized to avoid race conditions. |
| ANGLE_TRY(mRenderer->ensureHLSLCompilerInitialized(GetImplAs<ContextD3D>(context))); |
| |
| *linkTaskOut = std::shared_ptr<LinkTask>(new LinkTaskD3D( |
| clientVersion, caps, clientType, this, context->getState().getProvokingVertex())); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result ProgramD3D::linkJobImpl(d3d::Context *context, |
| const gl::Caps &caps, |
| const gl::Version &clientVersion, |
| EGLenum clientType, |
| const gl::ProgramLinkedResources &resources, |
| const gl::ProgramMergedVaryings &mergedVaryings) |
| { |
| ProgramExecutableD3D *executableD3D = getExecutable(); |
| |
| const gl::SharedCompiledShaderState &computeShader = |
| mState.getAttachedShader(gl::ShaderType::Compute); |
| if (computeShader) |
| { |
| const gl::SharedCompiledShaderState &shader = |
| mState.getAttachedShader(gl::ShaderType::Compute); |
| executableD3D->mShaderHLSL[gl::ShaderType::Compute] = shader->translatedSource; |
| |
| executableD3D->mShaderSamplers[gl::ShaderType::Compute].resize( |
| caps.maxShaderTextureImageUnits[gl::ShaderType::Compute]); |
| executableD3D->mImages[gl::ShaderType::Compute].resize(caps.maxImageUnits); |
| executableD3D->mReadonlyImages[gl::ShaderType::Compute].resize(caps.maxImageUnits); |
| |
| executableD3D->mShaderUniformsDirty.set(gl::ShaderType::Compute); |
| |
| linkResources(resources); |
| |
| for (const sh::ShaderVariable &uniform : computeShader->uniforms) |
| { |
| if (gl::IsImageType(uniform.type) && gl::IsImage2DType(uniform.type)) |
| { |
| executableD3D->mImage2DUniforms[gl::ShaderType::Compute].push_back(uniform); |
| } |
| } |
| |
| executableD3D->defineUniformsAndAssignRegisters(mRenderer, mState.getAttachedShaders()); |
| |
| return angle::Result::Continue; |
| } |
| |
| for (gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes) |
| { |
| const gl::SharedCompiledShaderState &shader = mState.getAttachedShader(shaderType); |
| if (shader) |
| { |
| executableD3D->mAttachedShaders[shaderType]->generateWorkarounds( |
| &executableD3D->mShaderWorkarounds[shaderType]); |
| executableD3D->mShaderSamplers[shaderType].resize( |
| caps.maxShaderTextureImageUnits[shaderType]); |
| executableD3D->mImages[shaderType].resize(caps.maxImageUnits); |
| executableD3D->mReadonlyImages[shaderType].resize(caps.maxImageUnits); |
| |
| executableD3D->mShaderUniformsDirty.set(shaderType); |
| |
| for (const sh::ShaderVariable &uniform : shader->uniforms) |
| { |
| if (gl::IsImageType(uniform.type) && gl::IsImage2DType(uniform.type)) |
| { |
| executableD3D->mImage2DUniforms[shaderType].push_back(uniform); |
| } |
| } |
| |
| for (const std::string &slowBlock : |
| executableD3D->mAttachedShaders[shaderType]->slowCompilingUniformBlockSet) |
| { |
| WARN() << "Uniform block '" << slowBlock << "' will be slow to compile. " |
| << "See UniformBlockToStructuredBufferTranslation.md " |
| << "(https://shorturl.at/drFY7) for details."; |
| } |
| } |
| } |
| |
| if (mRenderer->getNativeLimitations().noFrontFacingSupport) |
| { |
| const SharedCompiledShaderStateD3D &fragmentShader = |
| executableD3D->mAttachedShaders[gl::ShaderType::Fragment]; |
| if (fragmentShader && fragmentShader->usesFrontFacing) |
| { |
| mState.getExecutable().getInfoLog() |
| << "The current renderer doesn't support gl_FrontFacing"; |
| // Fail compilation |
| ANGLE_CHECK_HR(context, false, "gl_FrontFacing not supported", E_NOTIMPL); |
| } |
| } |
| |
| const gl::VaryingPacking &varyingPacking = |
| resources.varyingPacking.getOutputPacking(gl::ShaderType::Vertex); |
| |
| ProgramD3DMetadata metadata(mRenderer, mState.getAttachedShader(gl::ShaderType::Fragment), |
| executableD3D->mAttachedShaders, clientType, |
| mState.getAttachedShader(gl::ShaderType::Vertex)->shaderVersion); |
| BuiltinVaryingsD3D builtins(metadata, varyingPacking); |
| |
| DynamicHLSL::GenerateShaderLinkHLSL(mRenderer, caps, mState.getAttachedShaders(), |
| executableD3D->mAttachedShaders, metadata, varyingPacking, |
| builtins, &executableD3D->mShaderHLSL); |
| |
| executableD3D->mUsesPointSize = |
| executableD3D->mAttachedShaders[gl::ShaderType::Vertex] && |
| executableD3D->mAttachedShaders[gl::ShaderType::Vertex]->usesPointSize; |
| DynamicHLSL::GetPixelShaderOutputKey(mRenderer, caps, clientVersion, mState.getExecutable(), |
| metadata, &executableD3D->mPixelShaderKey); |
| executableD3D->mFragDepthUsage = metadata.getFragDepthUsage(); |
| executableD3D->mUsesSampleMask = metadata.usesSampleMask(); |
| executableD3D->mUsesVertexID = metadata.usesVertexID(); |
| executableD3D->mUsesViewID = metadata.usesViewID(); |
| executableD3D->mHasMultiviewEnabled = metadata.hasMultiviewEnabled(); |
| |
| // Cache if we use flat shading |
| executableD3D->mUsesFlatInterpolation = |
| FindFlatInterpolationVarying(mState.getAttachedShaders()); |
| |
| if (mRenderer->getMajorShaderModel() >= 4) |
| { |
| executableD3D->mGeometryShaderPreamble = DynamicHLSL::GenerateGeometryShaderPreamble( |
| mRenderer, varyingPacking, builtins, executableD3D->mHasMultiviewEnabled, |
| metadata.canSelectViewInVertexShader()); |
| } |
| |
| executableD3D->initAttribLocationsToD3DSemantic( |
| mState.getAttachedShader(gl::ShaderType::Vertex)); |
| |
| executableD3D->defineUniformsAndAssignRegisters(mRenderer, mState.getAttachedShaders()); |
| |
| executableD3D->gatherTransformFeedbackVaryings(mRenderer, varyingPacking, |
| mState.getTransformFeedbackVaryingNames(), |
| builtins[gl::ShaderType::Vertex]); |
| |
| linkResources(resources); |
| |
| if (mState.getAttachedShader(gl::ShaderType::Vertex)) |
| { |
| executableD3D->updateCachedInputLayoutFromShader( |
| mRenderer, mState.getAttachedShader(gl::ShaderType::Vertex)); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| GLboolean ProgramD3D::validate(const gl::Caps & /*caps*/) |
| { |
| // TODO(jmadill): Do something useful here? |
| return GL_TRUE; |
| } |
| |
| void ProgramD3D::linkResources(const gl::ProgramLinkedResources &resources) |
| { |
| HLSLBlockLayoutEncoderFactory hlslEncoderFactory; |
| gl::ProgramLinkedResourcesLinker linker(&hlslEncoderFactory); |
| |
| linker.linkResources(mState, resources); |
| |
| ProgramExecutableD3D *executableD3D = getExecutable(); |
| |
| executableD3D->initializeUniformBlocks(); |
| executableD3D->initializeShaderStorageBlocks(mState.getAttachedShaders()); |
| } |
| |
| } // namespace rx |