Files
ppsspp/GPU/D3D11/D3D11Util.cpp
T
Henrik Rydgård 425f6e2c37 D3D11: Don't silently draw with a shader that failed to compile
The shader creation helpers returned S_FALSE when compilation produced no
bytecode - but S_FALSE is a success code, so the FAILED() checks in the
D3D11VertexShader/D3D11FragmentShader constructors never fired and failed_ was
never set. Return E_FAIL instead, and only hand out the bytecode when the shader
object was actually created.

Failed() had no callers at all, so a failed shader was handed to the draw as
usual: VSSetShader(nullptr), CreateInputLayout on empty bytecode, and the
ignored HRESULT from SetupDecFmtForDraw meant IASetInputLayout(nullptr). The
draw then did nothing, with no log line to explain it. Skip the draw and warn
instead, like the GL backend does.
2026-09-04 12:08:21 -06:00

116 lines
4.1 KiB
C++

#include "ppsspp_config.h"
#include <cstdint>
#include <cfloat>
#include <vector>
#include <string>
#include <d3d11.h>
#include <D3Dcompiler.h>
#if PPSSPP_PLATFORM(UWP)
#define ptr_D3DCompile D3DCompile
#else
#include "Common/GPU/D3D11/D3D11Loader.h"
#endif
#include "Common/CommonFuncs.h"
#include "Common/Log.h"
#include "Common/StringUtils.h"
#include "D3D11Util.h"
#include <wrl/client.h>
using namespace Microsoft::WRL;
std::vector<uint8_t> CompileShaderToBytecodeD3D11(const char *code, size_t codeSize, const char *target, UINT flags, std::string *errorMessage) {
ComPtr<ID3DBlob> compiledCode;
ComPtr<ID3DBlob> errorMsgs;
HRESULT result = ptr_D3DCompile(code, codeSize, nullptr, nullptr, nullptr, "main", target, flags, 0, &compiledCode, &errorMsgs);
std::string errors;
if (errorMsgs) {
errors = std::string((const char *)errorMsgs->GetBufferPointer(), errorMsgs->GetBufferSize() > 1 ? (errorMsgs->GetBufferSize() - 1) : 0);
std::string numberedCode = LineNumberString(code);
if (SUCCEEDED(result)) {
std::vector<std::string_view> lines;
SplitString(errors, '\n', lines);
for (auto &line : lines) {
auto trimmed = StripSpaces(line);
// Ignore the useless warning about taking the power of negative numbers.
if (trimmed.find("pow(f, e) will not work for negative f") != std::string::npos) {
continue;
}
if (trimmed.size() > 1) { // ignore single nulls, not sure how they appear.
WARN_LOG(Log::G3D, "%.*s", (int)trimmed.length(), trimmed.data());
}
}
} else {
ERROR_LOG(Log::G3D, "%s: %s\n\n%s", "errors", errors.c_str(), numberedCode.c_str());
if (errorMessage) {
*errorMessage = errors;
}
OutputDebugStringA(errors.c_str());
OutputDebugStringA(numberedCode.c_str());
}
}
if (compiledCode) {
// Success!
const uint8_t *buf = (const uint8_t *)compiledCode->GetBufferPointer();
std::vector<uint8_t> compiled = std::vector<uint8_t>(buf, buf + compiledCode->GetBufferSize());
_assert_(compiled.size() != 0);
return compiled;
}
return std::vector<uint8_t>();
}
HRESULT CreateVertexShaderD3D11(ID3D11Device *device, const char *code, size_t codeSize, std::vector<uint8_t> *byteCodeOut, D3D_FEATURE_LEVEL featureLevel, UINT flags, ID3D11VertexShader **ppVertexShader) {
if (ppVertexShader)
*ppVertexShader = nullptr;
const char *profile = featureLevel <= D3D_FEATURE_LEVEL_9_3 ? "vs_4_0_level_9_1" : "vs_4_0";
std::string errorMessage;
std::vector<uint8_t> byteCode = CompileShaderToBytecodeD3D11(code, codeSize, profile, flags, &errorMessage);
if (byteCode.empty()) {
if (!errorMessage.empty()) {
ERROR_LOG(Log::G3D, "%s", errorMessage.c_str());
}
return E_FAIL;
}
auto hr = device->CreateVertexShader(byteCode.data(), byteCode.size(), nullptr, ppVertexShader);
if (SUCCEEDED(hr) && byteCodeOut)
*byteCodeOut = byteCode;
return hr;
}
HRESULT CreatePixelShaderD3D11(ID3D11Device *device, const char *code, size_t codeSize, D3D_FEATURE_LEVEL featureLevel, UINT flags, ID3D11PixelShader **ppPixelShader) {
if (ppPixelShader)
*ppPixelShader = nullptr;
const char *profile = featureLevel <= D3D_FEATURE_LEVEL_9_3 ? "ps_4_0_level_9_1" : "ps_4_0";
std::string errorMessage;
std::vector<uint8_t> byteCode = CompileShaderToBytecodeD3D11(code, codeSize, profile, flags, &errorMessage);
if (byteCode.empty()) {
if (!errorMessage.empty()) {
ERROR_LOG(Log::G3D, "%s", errorMessage.c_str());
}
return E_FAIL;
}
return device->CreatePixelShader(byteCode.data(), byteCode.size(), nullptr, ppPixelShader);
}
HRESULT CreateComputeShaderD3D11(ID3D11Device *device, const char *code, size_t codeSize, D3D_FEATURE_LEVEL featureLevel, UINT flags, ID3D11ComputeShader **ppComputeShader) {
if (ppComputeShader)
*ppComputeShader = nullptr;
if (featureLevel <= D3D_FEATURE_LEVEL_9_3)
return E_FAIL;
std::string errorMessage;
std::vector<uint8_t> byteCode = CompileShaderToBytecodeD3D11(code, codeSize, "cs_4_0", flags, &errorMessage);
if (byteCode.empty()) {
if (!errorMessage.empty()) {
ERROR_LOG(Log::G3D, "%s", errorMessage.c_str());
}
return E_FAIL;
}
return device->CreateComputeShader(byteCode.data(), byteCode.size(), nullptr, ppComputeShader);
}