blob: eda96dc5f4e669746ad3487af846e406d6931dd0 [file] [log] [blame]
//
// Copyright 2024 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.
//
// ProgramWgpu.cpp:
// Implements the class methods for ProgramWgpu.
//
#include "libANGLE/renderer/wgpu/ProgramWgpu.h"
#include "GLES2/gl2.h"
#include "common/PackedEnums.h"
#include "common/PackedGLEnums_autogen.h"
#include "common/debug.h"
#include "common/log_utils.h"
#include "libANGLE/Error.h"
#include "libANGLE/ProgramExecutable.h"
#include "libANGLE/renderer/wgpu/ProgramExecutableWgpu.h"
#include "libANGLE/renderer/wgpu/wgpu_utils.h"
#include "libANGLE/renderer/wgpu/wgpu_wgsl_util.h"
#include "libANGLE/trace.h"
#include <dawn/webgpu_cpp.h>
namespace rx
{
namespace
{
const bool kOutputFinalSource = false;
// Identical to Std140 encoder in all aspects, except it ignores opaque uniform types.
class WgpuDefaultBlockEncoder : public sh::Std140BlockEncoder
{
public:
void advanceOffset(GLenum type,
const std::vector<unsigned int> &arraySizes,
bool isRowMajorMatrix,
int arrayStride,
int matrixStride) override
{
if (gl::IsOpaqueType(type))
{
return;
}
sh::Std140BlockEncoder::advanceOffset(type, arraySizes, isRowMajorMatrix, arrayStride,
matrixStride);
}
};
void InitDefaultUniformBlock(const std::vector<sh::ShaderVariable> &uniforms,
sh::BlockLayoutMap *blockLayoutMapOut,
size_t *blockSizeOut)
{
if (uniforms.empty())
{
*blockSizeOut = 0;
return;
}
WgpuDefaultBlockEncoder blockEncoder;
sh::GetActiveUniformBlockInfo(uniforms, "", &blockEncoder, blockLayoutMapOut);
*blockSizeOut = blockEncoder.getCurrentOffset();
return;
}
class CreateWGPUShaderModuleTask : public LinkSubTask
{
public:
CreateWGPUShaderModuleTask(wgpu::Instance instance,
wgpu::Device device,
const gl::SharedCompiledShaderState &compiledShaderState,
const gl::ProgramExecutable &executable,
gl::ProgramMergedVaryings mergedVaryings,
TranslatedWGPUShaderModule &resultShaderModule)
: mInstance(instance),
mDevice(device),
mCompiledShaderState(compiledShaderState),
mExecutable(executable),
mMergedVaryings(std::move(mergedVaryings)),
mShaderModule(resultShaderModule)
{}
angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
{
infoLog << mLog.str();
return mResult;
}
void operator()() override
{
ANGLE_TRACE_EVENT0("gpu.angle", "CreateWGPUShaderModuleTask");
gl::ShaderType shaderType = mCompiledShaderState->shaderType;
ASSERT((mExecutable.getLinkedShaderStages() &
~gl::ShaderBitSet({gl::ShaderType::Vertex, gl::ShaderType::Fragment}))
.none());
std::string finalShaderSource;
if (shaderType == gl::ShaderType::Vertex)
{
finalShaderSource = webgpu::WgslAssignLocations(mCompiledShaderState->translatedSource,
mExecutable.getProgramInputs(),
mMergedVaryings, shaderType);
}
else if (shaderType == gl::ShaderType::Fragment)
{
finalShaderSource = webgpu::WgslAssignLocations(mCompiledShaderState->translatedSource,
mExecutable.getOutputVariables(),
mMergedVaryings, shaderType);
}
else
{
UNIMPLEMENTED();
}
if (kOutputFinalSource)
{
std::cout << finalShaderSource;
}
wgpu::ShaderModuleWGSLDescriptor shaderModuleWGSLDescriptor;
shaderModuleWGSLDescriptor.code = finalShaderSource.c_str();
wgpu::ShaderModuleDescriptor shaderModuleDescriptor;
shaderModuleDescriptor.nextInChain = &shaderModuleWGSLDescriptor;
mShaderModule.module = mDevice.CreateShaderModule(&shaderModuleDescriptor);
wgpu::CompilationInfoCallback<CreateWGPUShaderModuleTask *> *getCompilationInfoCallback =
[](wgpu::CompilationInfoRequestStatus status,
wgpu::CompilationInfo const *compilationInfo, CreateWGPUShaderModuleTask *task) {
if (status != wgpu::CompilationInfoRequestStatus::Success)
{
task->mResult = angle::Result::Stop;
}
for (size_t msgIdx = 0; msgIdx < compilationInfo->messageCount; ++msgIdx)
{
const wgpu::CompilationMessage &message = compilationInfo->messages[msgIdx];
switch (message.type)
{
case wgpu::CompilationMessageType::Error:
task->mLog << "Error: ";
break;
case wgpu::CompilationMessageType::Warning:
task->mLog << "Warning: ";
break;
case wgpu::CompilationMessageType::Info:
task->mLog << "Info: ";
break;
default:
task->mLog << "Unknown: ";
break;
}
task->mLog << message.lineNum << ":" << message.linePos << ": "
<< std::string(message.message) << std::endl;
}
};
wgpu::FutureWaitInfo waitInfo;
waitInfo.future = mShaderModule.module.GetCompilationInfo(wgpu::CallbackMode::WaitAnyOnly,
getCompilationInfoCallback, this);
wgpu::WaitStatus waitStatus = mInstance.WaitAny(1, &waitInfo, -1);
if (waitStatus != wgpu::WaitStatus::Success)
{
mResult = angle::Result::Stop;
}
}
private:
wgpu::Instance mInstance;
wgpu::Device mDevice;
gl::SharedCompiledShaderState mCompiledShaderState;
const gl::ProgramExecutable &mExecutable;
gl::ProgramMergedVaryings mMergedVaryings;
TranslatedWGPUShaderModule &mShaderModule;
std::ostringstream mLog;
angle::Result mResult = angle::Result::Continue;
};
class LinkTaskWgpu : public LinkTask
{
public:
LinkTaskWgpu(wgpu::Instance instance, wgpu::Device device, ProgramWgpu *program)
: mInstance(instance),
mDevice(device),
mProgram(program),
mExecutable(&mProgram->getState().getExecutable())
{}
~LinkTaskWgpu() override = default;
void link(const gl::ProgramLinkedResources &resources,
const gl::ProgramMergedVaryings &mergedVaryings,
std::vector<std::shared_ptr<LinkSubTask>> *linkSubTasksOut,
std::vector<std::shared_ptr<LinkSubTask>> *postLinkSubTasksOut) override
{
ASSERT(linkSubTasksOut && linkSubTasksOut->empty());
ASSERT(postLinkSubTasksOut && postLinkSubTasksOut->empty());
ProgramExecutableWgpu *executable =
GetImplAs<ProgramExecutableWgpu>(&mProgram->getState().getExecutable());
const gl::ShaderMap<gl::SharedCompiledShaderState> &shaders =
mProgram->getState().getAttachedShaders();
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
if (shaders[shaderType])
{
auto task = std::make_shared<CreateWGPUShaderModuleTask>(
mInstance, mDevice, shaders[shaderType], *executable->getExecutable(),
mergedVaryings, executable->getShaderModule(shaderType));
linkSubTasksOut->push_back(task);
}
}
// The default uniform block's CPU buffer needs to be allocated and the layout calculated,
// now that the list of uniforms is known.
angle::Result initUniformBlocksResult = initDefaultUniformBlocks();
if (IsError(initUniformBlocksResult))
{
mLinkResult = initUniformBlocksResult;
return;
}
mLinkResult = angle::Result::Continue;
}
angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
{
return mLinkResult;
}
private:
angle::Result initDefaultUniformBlocks()
{
ProgramExecutableWgpu *executableWgpu = webgpu::GetImpl(mExecutable);
// Process vertex and fragment uniforms into std140 packing.
gl::ShaderMap<sh::BlockLayoutMap> layoutMap;
gl::ShaderMap<size_t> requiredBufferSize;
requiredBufferSize.fill(0);
generateUniformLayoutMapping(&layoutMap, &requiredBufferSize);
initDefaultUniformLayoutMapping(&layoutMap);
// All uniform initializations are complete, now resize the buffers accordingly and return
ANGLE_TRY(executableWgpu->resizeUniformBlockMemory(requiredBufferSize));
executableWgpu->markDefaultUniformsDirty();
return angle::Result::Continue;
}
void generateUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> *layoutMapOut,
gl::ShaderMap<size_t> *requiredBufferSizeOut)
{
for (const gl::ShaderType shaderType : mExecutable->getLinkedShaderStages())
{
const gl::SharedCompiledShaderState &shader =
mProgram->getState().getAttachedShader(shaderType);
if (shader)
{
const std::vector<sh::ShaderVariable> &uniforms = shader->uniforms;
InitDefaultUniformBlock(uniforms, &(*layoutMapOut)[shaderType],
&(*requiredBufferSizeOut)[shaderType]);
}
}
}
void initDefaultUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> *layoutMapOut)
{
// Init the default block layout info.
ProgramExecutableWgpu *executableWgpu = webgpu::GetImpl(mExecutable);
const auto &uniforms = mExecutable->getUniforms();
for (const gl::VariableLocation &location : mExecutable->getUniformLocations())
{
gl::ShaderMap<sh::BlockMemberInfo> layoutInfo;
if (location.used() && !location.ignored)
{
const auto &uniform = uniforms[location.index];
if (uniform.isInDefaultBlock() && !uniform.isSampler() && !uniform.isImage() &&
!uniform.isFragmentInOut())
{
std::string uniformName = mExecutable->getUniformNameByIndex(location.index);
if (uniform.isArray())
{
// Gets the uniform name without the [0] at the end.
uniformName = gl::StripLastArrayIndex(uniformName);
ASSERT(uniformName.size() !=
mExecutable->getUniformNameByIndex(location.index).size());
}
bool found = false;
for (const gl::ShaderType shaderType : mExecutable->getLinkedShaderStages())
{
auto it = (*layoutMapOut)[shaderType].find(uniformName);
if (it != (*layoutMapOut)[shaderType].end())
{
found = true;
layoutInfo[shaderType] = it->second;
}
}
ASSERT(found);
}
}
for (const gl::ShaderType shaderType : mExecutable->getLinkedShaderStages())
{
executableWgpu->getSharedDefaultUniformBlock(shaderType)
->uniformLayout.push_back(layoutInfo[shaderType]);
}
}
}
wgpu::Instance mInstance;
wgpu::Device mDevice;
ProgramWgpu *mProgram = nullptr;
const gl::ProgramExecutable *mExecutable;
angle::Result mLinkResult = angle::Result::Stop;
};
} // anonymous namespace
ProgramWgpu::ProgramWgpu(const gl::ProgramState &state) : ProgramImpl(state) {}
ProgramWgpu::~ProgramWgpu() {}
angle::Result ProgramWgpu::load(const gl::Context *context,
gl::BinaryInputStream *stream,
std::shared_ptr<LinkTask> *loadTaskOut,
egl::CacheGetResult *resultOut)
{
*loadTaskOut = {};
*resultOut = egl::CacheGetResult::Success;
return angle::Result::Continue;
}
void ProgramWgpu::save(const gl::Context *context, gl::BinaryOutputStream *stream) {}
void ProgramWgpu::setBinaryRetrievableHint(bool retrievable) {}
void ProgramWgpu::setSeparable(bool separable) {}
angle::Result ProgramWgpu::link(const gl::Context *context, std::shared_ptr<LinkTask> *linkTaskOut)
{
wgpu::Device device = webgpu::GetDevice(context);
wgpu::Instance instance = webgpu::GetInstance(context);
*linkTaskOut = std::shared_ptr<LinkTask>(new LinkTaskWgpu(instance, device, this));
return angle::Result::Continue;
}
GLboolean ProgramWgpu::validate(const gl::Caps &caps)
{
return GL_TRUE;
}
} // namespace rx