mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 21:21:17 +01:00
fix(ios): Don't call abort when the unexpected happens. (#10472) 1adc508ecd
* removed aborts and properly handle the error case instead. Don't use printf because nslog allows for proper output to log files in ios * more converts to nslog * made checks more specific * Wiring up multiple synthesized failure types * Wiring through the new synthesized failure modes to D3D11, 12, and GL * Move the Metal ubershaderLoad failure synthesis to a better spot * clang format * Missed wrapping a variable in #ifdef WITH_RIVE_TOOLS * Correction: missed *multiple* #ifdef WITH_RIVE_TOOLSes * Still more * Testing to see if the D3D12/GL errors are related to the ubershaderLoad synthesis or not * Removing additional pieces of the testing to see what's causing the issues * It's important to write your preprocessor directives correctly 🙃 * Trying to figure out what the fence value is that is coming out wrong on the D3D12 tests and why GL is failing * trying something dumb to see if this re-breaks the oneplus7 * Split the render test up into three tests to see if it is any better on the oneplus7 * Trying to see where the d3d12 device is getting removed (and why), and also what happens if I run 2 of the same synth test on oneplus7 * Sorry everyone it's effectively printf debugging time 🫤 * Changed the CreateEvent call to see if that works for D3D12 and also more printing for GL * More * Okay testing some other dumb stuff - this might resolve oneplus 7, still no idea on D3D12 yet * Testing an alternate fix for the oneplus7 issue plus a different initial frame value for the copy fence * Adding a comment before push * Clean up the testing code (the D3D and oneplus7 issues are fixed but now there's a GL issue on windows. sigh. * clang format again I'm good at this lol * Okay I think this will fix the windows GLFW issue at the cost of it might break all the android tests or something (but I hope not!) * Now debugging why glfw window creation is failing for windows unit tests * Okay this should "fix" the GL issues on github by just not creating a GL window if GL is not supported. * Some minor cleanup * Clarifying a comment, mostly to get the tests to re-kick Co-authored-by: Jonathon Copeland <jcopela4@gmail.com> Co-authored-by: Josh Jersild <joshua@rive.app>
This commit is contained in:
@@ -1 +1 @@
|
||||
b555c57747b50c47b02819206816f8dda2df3b57
|
||||
1adc508ecd865d2a7c8187ce6d6ce04f8d5229b3
|
||||
|
||||
@@ -43,7 +43,8 @@ public:
|
||||
rive::gpu::InterlockMode interlockMode;
|
||||
rive::gpu::ShaderMiscFlags shaderMiscFlags;
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
bool synthesizeCompilationFailures = false;
|
||||
rive::gpu::SynthesizedFailureType synthesizedFailureType =
|
||||
rive::gpu::SynthesizedFailureType::none;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -68,7 +69,7 @@ public:
|
||||
assert(!m_jobThread.joinable());
|
||||
}
|
||||
|
||||
const PipelineType& getPipeline(const PipelineProps& propsIn)
|
||||
const PipelineType* tryGetPipeline(const PipelineProps& propsIn)
|
||||
{
|
||||
PipelineProps props = propsIn;
|
||||
|
||||
@@ -111,10 +112,23 @@ public:
|
||||
auto iter = m_pipelines.find(key);
|
||||
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
// If requested, synthesize a complete failure to get an ubershader
|
||||
// (i.e. pretend we attempted to load the current shader asynchronously
|
||||
// and tried to fall back on an uber, which failed) (Don't fail on
|
||||
// "atomicResolve" because if we fail that one the unit test won't see
|
||||
// the clear color)
|
||||
if (props.synthesizedFailureType ==
|
||||
gpu::SynthesizedFailureType::ubershaderLoad &&
|
||||
props.drawType != DrawType::atomicResolve)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (props.shaderFeatures == ubershaderFeatures)
|
||||
{
|
||||
// Never synthesize compilation failure for an ubershader.
|
||||
props.synthesizeCompilationFailures = false;
|
||||
// Otherwise, do not synthesize compilation failure for an
|
||||
// ubershader.
|
||||
props.synthesizedFailureType = gpu::SynthesizedFailureType::none;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -140,11 +154,17 @@ public:
|
||||
|
||||
if (getPipelineStatus(*iter->second) != PipelineStatus::errored)
|
||||
{
|
||||
return *iter->second;
|
||||
return &*iter->second;
|
||||
}
|
||||
|
||||
// It's bad to have a creation error for an ubershader, but
|
||||
// otherwise we can still fall back on the ubershader.
|
||||
if (props.shaderFeatures == ubershaderFeatures)
|
||||
{
|
||||
// Ubershader creation failed
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// This pipeline failed to build for some reason, but we can
|
||||
// (potentially) fall back on the ubershader.
|
||||
assert(props.shaderFeatures != ubershaderFeatures);
|
||||
}
|
||||
}
|
||||
@@ -171,7 +191,7 @@ public:
|
||||
status == PipelineStatus::ready)
|
||||
{
|
||||
// The program is present and ready to go!
|
||||
return *iter->second;
|
||||
return &*iter->second;
|
||||
}
|
||||
else if (status != PipelineStatus::errored)
|
||||
{
|
||||
@@ -182,7 +202,7 @@ public:
|
||||
if (advanceCreation(*iter->second, props))
|
||||
{
|
||||
// The program was not previously ready, but it is now.
|
||||
return *iter->second;
|
||||
return &*iter->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,13 +211,13 @@ public:
|
||||
// version (with all functionality enabled). This will create
|
||||
// synchronously so we're guaranteed to have a valid return from this
|
||||
// call.
|
||||
// NOTE: intentionally not passing along synthesizeCompilationFailures
|
||||
// NOTE: intentionally not passing along synthesizedFailureType
|
||||
// here because we don't pay attention to it for ubershaders anyway
|
||||
assert(props.shaderFeatures != ubershaderFeatures);
|
||||
return getPipeline({props.drawType,
|
||||
ubershaderFeatures,
|
||||
props.interlockMode,
|
||||
props.shaderMiscFlags});
|
||||
return tryGetPipeline({props.drawType,
|
||||
ubershaderFeatures,
|
||||
props.interlockMode,
|
||||
props.shaderMiscFlags});
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -92,13 +92,13 @@ public:
|
||||
|
||||
~D3D11PipelineManager() { shutdownBackgroundThread(); }
|
||||
|
||||
void setPipelineState(rive::gpu::DrawType,
|
||||
rive::gpu::ShaderFeatures,
|
||||
rive::gpu::InterlockMode,
|
||||
rive::gpu::ShaderMiscFlags
|
||||
[[nodiscard]] bool setPipelineState(rive::gpu::DrawType,
|
||||
rive::gpu::ShaderFeatures,
|
||||
rive::gpu::InterlockMode,
|
||||
rive::gpu::ShaderMiscFlags
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
,
|
||||
bool synthesizeCompilationFailures
|
||||
,
|
||||
SynthesizedFailureType
|
||||
#endif
|
||||
);
|
||||
|
||||
|
||||
@@ -321,7 +321,7 @@ private:
|
||||
gpu::ShaderMiscFlags
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
,
|
||||
bool synthesizeCompilationFailures
|
||||
SynthesizedFailureType
|
||||
#endif
|
||||
);
|
||||
~DrawProgram();
|
||||
@@ -372,6 +372,10 @@ private:
|
||||
GLuint m_id = 0;
|
||||
GLint m_baseInstanceUniformLocation = -1;
|
||||
const rcp<GLState> m_state;
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
SynthesizedFailureType m_synthesizedFailureType =
|
||||
SynthesizedFailureType::none;
|
||||
#endif
|
||||
};
|
||||
|
||||
class GLPipelineManager : public AsyncPipelineManager<DrawProgram>
|
||||
|
||||
@@ -1051,6 +1051,18 @@ struct TwoTexelRamp
|
||||
};
|
||||
static_assert(sizeof(TwoTexelRamp) == 8 * sizeof(uint8_t));
|
||||
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
|
||||
enum class SynthesizedFailureType
|
||||
{
|
||||
none,
|
||||
ubershaderLoad,
|
||||
shaderCompilation,
|
||||
pipelineCreation,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// Detailed description of exactly how a RenderContextImpl should bind its
|
||||
// buffers and draw a flush. A typical flush is done in 4 steps:
|
||||
//
|
||||
@@ -1132,7 +1144,8 @@ struct FlushDescriptor
|
||||
// gracefully. (e.g., by falling back on an uber shader or at least not
|
||||
// crashing.) Valid compilations may fail in the real world if the device is
|
||||
// pressed for resources or in a bad state.
|
||||
bool synthesizeCompilationFailures = false;
|
||||
SynthesizedFailureType synthesizedFailureType =
|
||||
SynthesizedFailureType::none;
|
||||
#endif
|
||||
|
||||
// Command buffer that rendering commands will be added to.
|
||||
|
||||
@@ -101,6 +101,11 @@ public:
|
||||
// m_platformFeatures.supportsRasterOrdering to false, forcing us to
|
||||
// always render in atomic mode.
|
||||
bool disableFramebufferReads = false;
|
||||
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
SynthesizedFailureType synthesizedFailureType =
|
||||
SynthesizedFailureType::none;
|
||||
#endif
|
||||
};
|
||||
|
||||
static std::unique_ptr<RenderContext> MakeContext(id<MTLDevice>,
|
||||
|
||||
@@ -117,7 +117,8 @@ public:
|
||||
// gracefully. (e.g., by falling back on an uber shader or at least not
|
||||
// crashing.) Valid compilations may fail in the real world if the
|
||||
// device is pressed for resources or in a bad state.
|
||||
bool synthesizeCompilationFailures = false;
|
||||
gpu::SynthesizedFailureType synthesizedFailureType =
|
||||
gpu::SynthesizedFailureType::none;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -156,25 +156,44 @@ public:
|
||||
ID3D12CommandList* ppCommandLists[] = {m_copyCommandList.Get()};
|
||||
m_copyCommandQueue->ExecuteCommandLists(1, ppCommandLists);
|
||||
|
||||
// set the initial state to 0 and inc frame to 1
|
||||
VERIFY_OK(m_device->CreateFence(m_currentFrame++,
|
||||
VERIFY_OK(m_device->CreateFence(m_currentFrame,
|
||||
D3D12_FENCE_FLAG_NONE,
|
||||
IID_PPV_ARGS(&m_fence)));
|
||||
|
||||
// set the initial state to -1 so that we can wait for 0
|
||||
VERIFY_OK(m_device->CreateFence(-1,
|
||||
// NOTE: Originally the code was setting this to -1 and then waiting on
|
||||
// a signal of 0, but this does not work in practice because some D3D12
|
||||
// implementations only allow the signaled value to increase - so
|
||||
// starting the fence at unsigned -1 meant that it can never be changed
|
||||
// again.
|
||||
VERIFY_OK(m_device->CreateFence(m_currentFrame,
|
||||
D3D12_FENCE_FLAG_NONE,
|
||||
IID_PPV_ARGS(&m_copyFence)));
|
||||
|
||||
// Increment m_currentFrame since the value we initialized the fences to
|
||||
// cannot be waited on (it'll return immediately).
|
||||
m_currentFrame++;
|
||||
|
||||
m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
VERIFY_OK(HRESULT_FROM_WIN32(GetLastError()));
|
||||
|
||||
assert(m_fenceEvent);
|
||||
VERIFY_OK(m_copyCommandQueue->Signal(m_copyFence.Get(), 0));
|
||||
VERIFY_OK(m_copyFence->SetEventOnCompletion(0, m_fenceEvent));
|
||||
WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
|
||||
|
||||
assert(m_copyFence->GetCompletedValue() == 0);
|
||||
// Signal the fence to ensure that we wait for creation to be complete.
|
||||
// Start at m_currentFrame which should currently be set to 1.
|
||||
VERIFY_OK(
|
||||
m_copyCommandQueue->Signal(m_copyFence.Get(), m_currentFrame));
|
||||
|
||||
if (m_copyFence->GetCompletedValue() != m_currentFrame)
|
||||
{
|
||||
VERIFY_OK(m_copyFence->SetEventOnCompletion(m_currentFrame,
|
||||
m_fenceEvent));
|
||||
WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
|
||||
|
||||
assert(m_copyFence->GetCompletedValue() == m_currentFrame);
|
||||
}
|
||||
|
||||
// Increment the current frame one more to get past this wait.
|
||||
m_currentFrame++;
|
||||
}
|
||||
|
||||
float dpiScale(GLFWwindow*) const override { return 1; }
|
||||
@@ -319,9 +338,9 @@ public:
|
||||
auto copySafeFrame = m_copyFence->GetCompletedValue();
|
||||
if (copySafeFrame < m_previousFrames[m_frameIndex])
|
||||
{
|
||||
VERIFY_OK(
|
||||
m_fence->SetEventOnCompletion(m_previousFrames[m_frameIndex],
|
||||
m_fenceEvent));
|
||||
VERIFY_OK(m_copyFence->SetEventOnCompletion(
|
||||
m_previousFrames[m_frameIndex],
|
||||
m_fenceEvent));
|
||||
WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
|
||||
copySafeFrame = m_previousFrames[m_frameIndex];
|
||||
}
|
||||
@@ -339,7 +358,7 @@ public:
|
||||
VERIFY_OK(m_fence->SetEventOnCompletion(frame, m_fenceEvent));
|
||||
WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
|
||||
|
||||
VERIFY_OK(m_fence->SetEventOnCompletion(frame, m_fenceEvent));
|
||||
VERIFY_OK(m_copyFence->SetEventOnCompletion(frame, m_fenceEvent));
|
||||
WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
|
||||
}
|
||||
|
||||
|
||||
@@ -218,31 +218,36 @@ D3D11PipelineManager::D3D11PipelineManager(
|
||||
&m_atlasStrokePixelShader));
|
||||
}
|
||||
|
||||
void D3D11PipelineManager::setPipelineState(
|
||||
bool D3D11PipelineManager::setPipelineState(
|
||||
rive::gpu::DrawType drawType,
|
||||
rive::gpu::ShaderFeatures features,
|
||||
rive::gpu::InterlockMode interlockMode,
|
||||
rive::gpu::ShaderMiscFlags miscFlags
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
,
|
||||
bool synthesizeCompilationFailures
|
||||
SynthesizedFailureType synthesizedFailureType
|
||||
#endif
|
||||
)
|
||||
{
|
||||
auto& result = getPipeline({
|
||||
auto* pipeline = tryGetPipeline({
|
||||
.drawType = drawType,
|
||||
.shaderFeatures = features,
|
||||
.interlockMode = interlockMode,
|
||||
.shaderMiscFlags = miscFlags,
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
.synthesizeCompilationFailures = synthesizeCompilationFailures,
|
||||
.synthesizedFailureType = synthesizedFailureType,
|
||||
#endif
|
||||
|
||||
});
|
||||
|
||||
m_context->IASetInputLayout(result.m_vertexShader.layout.Get());
|
||||
m_context->VSSetShader(result.m_vertexShader.shader.Get(), nullptr, 0);
|
||||
m_context->PSSetShader(result.m_pixelShader.Get(), nullptr, 0);
|
||||
if (pipeline == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_context->IASetInputLayout(pipeline->m_vertexShader.layout.Get());
|
||||
m_context->VSSetShader(pipeline->m_vertexShader.shader.Get(), nullptr, 0);
|
||||
m_context->PSSetShader(pipeline->m_pixelShader.Get(), nullptr, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
D3D11DrawVertexShader D3D11PipelineManager ::compileVertexShaderBlobToFinalType(
|
||||
@@ -360,7 +365,10 @@ D3D11DrawPipeline D3D11PipelineManager::linkPipeline(
|
||||
// For D3D11 this just puts the vs and ps into a single structure together.
|
||||
D3D11DrawPipeline pipeline;
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
if (props.synthesizeCompilationFailures)
|
||||
if (props.synthesizedFailureType ==
|
||||
SynthesizedFailureType::pipelineCreation ||
|
||||
props.synthesizedFailureType ==
|
||||
SynthesizedFailureType::shaderCompilation)
|
||||
{
|
||||
// An empty result is what counts as "failed"
|
||||
return pipeline;
|
||||
@@ -1824,6 +1832,7 @@ void RenderContextD3DImpl::flush(const FlushDescriptor& desc)
|
||||
for (const DrawBatch& batch : *desc.drawList)
|
||||
{
|
||||
DrawType drawType = batch.drawType;
|
||||
|
||||
auto shaderFeatures = desc.interlockMode == gpu::InterlockMode::atomics
|
||||
? desc.combinedShaderFeatures
|
||||
: batch.shaderFeatures;
|
||||
@@ -1844,15 +1853,20 @@ void RenderContextD3DImpl::flush(const FlushDescriptor& desc)
|
||||
shaderMiscFlags |= gpu::ShaderMiscFlags::clockwiseFill;
|
||||
}
|
||||
|
||||
m_pipelineManager.setPipelineState(drawType,
|
||||
shaderFeatures,
|
||||
desc.interlockMode,
|
||||
shaderMiscFlags
|
||||
if (!m_pipelineManager.setPipelineState(drawType,
|
||||
shaderFeatures,
|
||||
desc.interlockMode,
|
||||
shaderMiscFlags
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
,
|
||||
desc.synthesizeCompilationFailures
|
||||
,
|
||||
desc.synthesizedFailureType
|
||||
#endif
|
||||
);
|
||||
))
|
||||
{
|
||||
// There was an issue getting either the requested pipeline state or
|
||||
// its ubershader counterpart so we cannot draw anything.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto imageTextureD3D =
|
||||
static_cast<const TextureD3DImpl*>(batch.imageTexture))
|
||||
|
||||
@@ -235,7 +235,10 @@ D3D12Pipeline D3D12PipelineManager::linkPipeline(const PipelineProps& props,
|
||||
|
||||
D3D12Pipeline result;
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
if (props.synthesizeCompilationFailures)
|
||||
if (props.synthesizedFailureType ==
|
||||
SynthesizedFailureType::pipelineCreation ||
|
||||
props.synthesizedFailureType ==
|
||||
SynthesizedFailureType::shaderCompilation)
|
||||
{
|
||||
// An empty result is what counts as "failed"
|
||||
return result;
|
||||
|
||||
@@ -1415,16 +1415,24 @@ void RenderContextD3D12Impl::flush(const FlushDescriptor& desc)
|
||||
shaderMiscFlags |= gpu::ShaderMiscFlags::clockwiseFill;
|
||||
}
|
||||
|
||||
auto pipeline = m_pipelineManager.getPipeline({
|
||||
auto* pipeline = m_pipelineManager.tryGetPipeline({
|
||||
.drawType = drawType,
|
||||
.shaderFeatures = shaderFeatures,
|
||||
.interlockMode = desc.interlockMode,
|
||||
.shaderMiscFlags = shaderMiscFlags,
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
.synthesizeCompilationFailures = desc.synthesizeCompilationFailures,
|
||||
.synthesizedFailureType = desc.synthesizedFailureType,
|
||||
#endif
|
||||
});
|
||||
cmdList->SetPipelineState(pipeline.m_d3dPipelineState.Get());
|
||||
|
||||
if (pipeline == nullptr)
|
||||
{
|
||||
// There was an issue getting either the requested pipeline state or
|
||||
// its ubershader counterpart so we cannot draw anything.
|
||||
continue;
|
||||
}
|
||||
|
||||
cmdList->SetPipelineState(pipeline->m_d3dPipelineState.Get());
|
||||
|
||||
// all atomic barriers are the same for dx12
|
||||
if (batch.barriers &
|
||||
|
||||
@@ -1223,7 +1223,7 @@ RenderContextGLImpl::DrawProgram::DrawProgram(
|
||||
gpu::ShaderMiscFlags shaderMiscFlags
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
,
|
||||
bool synthesizeCompilationFailures
|
||||
SynthesizedFailureType synthesizedFailureType
|
||||
#endif
|
||||
) :
|
||||
m_fragmentShader(renderContextImpl,
|
||||
@@ -1235,9 +1235,9 @@ RenderContextGLImpl::DrawProgram::DrawProgram(
|
||||
m_state(renderContextImpl->m_state)
|
||||
{
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
if (synthesizeCompilationFailures)
|
||||
m_synthesizedFailureType = synthesizedFailureType;
|
||||
if (m_synthesizedFailureType == SynthesizedFailureType::shaderCompilation)
|
||||
{
|
||||
// An empty result is what counts as "failed"
|
||||
m_creationState = CreationState::error;
|
||||
return;
|
||||
}
|
||||
@@ -1349,6 +1349,14 @@ bool RenderContextGLImpl::DrawProgram::advanceCreation(
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
if (m_synthesizedFailureType == SynthesizedFailureType::pipelineCreation)
|
||||
{
|
||||
m_creationState = CreationState::error;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
GLint successfullyLinked = 0;
|
||||
glGetProgramiv(m_id, GL_LINK_STATUS, &successfullyLinked);
|
||||
@@ -1959,16 +1967,23 @@ void RenderContextGLImpl::flush(const FlushDescriptor& desc)
|
||||
{
|
||||
shaderMiscFlags |= gpu::ShaderMiscFlags::clockwiseFill;
|
||||
}
|
||||
const DrawProgram& drawProgram = m_pipelineManager.getPipeline({
|
||||
const DrawProgram* drawProgram = m_pipelineManager.tryGetPipeline({
|
||||
.drawType = drawType,
|
||||
.shaderFeatures = shaderFeatures,
|
||||
.interlockMode = desc.interlockMode,
|
||||
.shaderMiscFlags = shaderMiscFlags,
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
.synthesizeCompilationFailures = desc.synthesizeCompilationFailures,
|
||||
.synthesizedFailureType = desc.synthesizedFailureType,
|
||||
#endif
|
||||
});
|
||||
m_state->bindProgram(drawProgram.id());
|
||||
if (drawProgram == nullptr)
|
||||
{
|
||||
// There was an issue getting either the requested draw program or
|
||||
// its ubershader counterpart so we cannot draw anything.
|
||||
continue;
|
||||
}
|
||||
|
||||
m_state->bindProgram(drawProgram->id());
|
||||
|
||||
if (auto imageTextureGL =
|
||||
static_cast<const TextureGLImpl*>(batch.imageTexture))
|
||||
@@ -2069,7 +2084,7 @@ void RenderContextGLImpl::flush(const FlushDescriptor& desc)
|
||||
gpu::PatchBaseIndex(drawType),
|
||||
batch.elementCount,
|
||||
batch.baseElement,
|
||||
drawProgram.baseInstanceUniformLocation());
|
||||
drawProgram->baseInstanceUniformLocation());
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2818,7 +2833,7 @@ std::unique_ptr<RenderContextGLImpl::DrawProgram> RenderContextGLImpl::
|
||||
props.shaderMiscFlags
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
,
|
||||
props.synthesizeCompilationFailures
|
||||
props.synthesizedFailureType
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ struct BackgroundCompileJob
|
||||
gpu::ShaderMiscFlags shaderMiscFlags;
|
||||
id<MTLLibrary> compiledLibrary = nil;
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
bool synthesizeCompilationFailure = false;
|
||||
gpu::SynthesizedFailureType synthesizedFailureType =
|
||||
gpu::SynthesizedFailureType::none;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -263,7 +263,8 @@ void BackgroundShaderCompiler::threadMain()
|
||||
}
|
||||
compileOptions.preprocessorMacros = defines;
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
if (job.synthesizeCompilationFailure)
|
||||
if (job.synthesizedFailureType ==
|
||||
SynthesizedFailureType::shaderCompilation)
|
||||
{
|
||||
assert(job.compiledLibrary == nil);
|
||||
}
|
||||
@@ -280,9 +281,10 @@ void BackgroundShaderCompiler::threadMain()
|
||||
if (job.compiledLibrary == nil)
|
||||
{
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
if (job.synthesizeCompilationFailure)
|
||||
if (job.synthesizedFailureType ==
|
||||
SynthesizedFailureType::shaderCompilation)
|
||||
{
|
||||
fprintf(stderr, "Synthesizing shader compilation failure...\n");
|
||||
NSLog(@"Synthesizing shader compilation failure...");
|
||||
}
|
||||
else
|
||||
#endif
|
||||
@@ -295,15 +297,16 @@ void BackgroundShaderCompiler::threadMain()
|
||||
std::string lineStr;
|
||||
while (std::getline(stream, lineStr, '\n'))
|
||||
{
|
||||
fprintf(stderr, "%4i| %s\n", lineNumber++, lineStr.c_str());
|
||||
NSLog(@"%4i| %s", lineNumber++, lineStr.c_str());
|
||||
}
|
||||
fprintf(stderr, "%s\n", err.localizedDescription.UTF8String);
|
||||
NSLog(@"%@", err.localizedDescription);
|
||||
}
|
||||
|
||||
fprintf(stderr, "Failed to compile shader.\n");
|
||||
NSLog(@"Failed to compile shader.");
|
||||
assert(false
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
|| job.synthesizeCompilationFailure
|
||||
|| job.synthesizedFailureType ==
|
||||
SynthesizedFailureType::shaderCompilation
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,15 +39,12 @@ namespace rive::gpu
|
||||
static id<MTLRenderPipelineState> make_pipeline_state(
|
||||
id<MTLDevice> gpu, MTLRenderPipelineDescriptor* desc)
|
||||
{
|
||||
NSError* err = [NSError errorWithDomain:@"pipeline_create"
|
||||
code:201
|
||||
userInfo:nil];
|
||||
NSError* err = nil;
|
||||
id<MTLRenderPipelineState> state =
|
||||
[gpu newRenderPipelineStateWithDescriptor:desc error:&err];
|
||||
if (!state)
|
||||
if (err)
|
||||
{
|
||||
fprintf(stderr, "%s\n", err.localizedDescription.UTF8String);
|
||||
abort();
|
||||
NSLog(@"make_pipeline_state error %@", err.localizedDescription);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
@@ -261,7 +258,12 @@ public:
|
||||
gpu::DrawType drawType,
|
||||
gpu::InterlockMode interlockMode,
|
||||
gpu::ShaderFeatures shaderFeatures,
|
||||
gpu::ShaderMiscFlags shaderMiscFlags)
|
||||
gpu::ShaderMiscFlags shaderMiscFlags
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
,
|
||||
gpu::SynthesizedFailureType synthesizedFailureType
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (library == nil)
|
||||
{
|
||||
@@ -270,6 +272,14 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
if (synthesizedFailureType == SynthesizedFailureType::pipelineCreation)
|
||||
{
|
||||
NSLog(@"Synthesizing pipeline creation failure...");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto makePipelineState = [=](id<MTLFunction> vertexMain,
|
||||
id<MTLFunction> fragmentMain,
|
||||
MTLPixelFormat pixelFormat) {
|
||||
@@ -568,16 +578,14 @@ RenderContextMetalImpl::RenderContextMetalImpl(
|
||||
#endif
|
||||
nil,
|
||||
nil);
|
||||
NSError* err = [NSError errorWithDomain:@"metallib_load"
|
||||
code:200
|
||||
userInfo:nil];
|
||||
NSError* err = nil;
|
||||
m_plsPrecompiledLibrary = [m_gpu newLibraryWithData:metallibData
|
||||
error:&err];
|
||||
if (m_plsPrecompiledLibrary == nil)
|
||||
if (err)
|
||||
{
|
||||
fprintf(stderr, "Failed to load pls metallib.\n");
|
||||
fprintf(stderr, "%s\n", err.localizedDescription.UTF8String);
|
||||
abort();
|
||||
NSLog(@"Failed to load pls metallib error: %@",
|
||||
err.localizedDescription);
|
||||
return;
|
||||
}
|
||||
|
||||
m_colorRampPipeline =
|
||||
@@ -658,7 +666,12 @@ RenderContextMetalImpl::RenderContextMetalImpl(
|
||||
drawType,
|
||||
gpu::InterlockMode::rasterOrdering,
|
||||
allShaderFeatures,
|
||||
shaderMiscFlags);
|
||||
shaderMiscFlags
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
,
|
||||
SynthesizedFailureType::none
|
||||
#endif
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -956,6 +969,15 @@ const RenderContextMetalImpl::DrawPipeline* RenderContextMetalImpl::
|
||||
shaderFeatures = fullyFeaturedPipelineFeatures;
|
||||
}
|
||||
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
if (desc.synthesizedFailureType == SynthesizedFailureType::ubershaderLoad)
|
||||
{
|
||||
// Pretend that the requested shader is not ready yet and the ubershader
|
||||
// compilation failed
|
||||
return nil;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t pipelineKey = gpu::ShaderUniqueKey(
|
||||
drawType, shaderFeatures, desc.interlockMode, shaderMiscFlags);
|
||||
auto pipelineIter = m_drawPipelines.find(pipelineKey);
|
||||
@@ -969,7 +991,7 @@ const RenderContextMetalImpl::DrawPipeline* RenderContextMetalImpl::
|
||||
.interlockMode = desc.interlockMode,
|
||||
.shaderMiscFlags = shaderMiscFlags,
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
.synthesizeCompilationFailure = desc.synthesizeCompilationFailures,
|
||||
.synthesizedFailureType = desc.synthesizedFailureType,
|
||||
#endif
|
||||
});
|
||||
pipelineIter = m_drawPipelines.insert({pipelineKey, nullptr}).first;
|
||||
@@ -1006,7 +1028,12 @@ const RenderContextMetalImpl::DrawPipeline* RenderContextMetalImpl::
|
||||
job.drawType,
|
||||
job.interlockMode,
|
||||
job.shaderFeatures,
|
||||
job.shaderMiscFlags);
|
||||
job.shaderMiscFlags
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
,
|
||||
desc.synthesizedFailureType
|
||||
#endif
|
||||
);
|
||||
if (jobKey == pipelineKey)
|
||||
{
|
||||
// The shader we wanted was actually done compiling and pending
|
||||
@@ -1181,6 +1208,19 @@ void RenderContextMetalImpl::flush(const FlushDescriptor& desc)
|
||||
// Render the color ramps to the gradient texture.
|
||||
if (desc.gradSpanCount > 0)
|
||||
{
|
||||
// We failed to load the precompiled library and therefore do not have
|
||||
// the abililty to draw anything.
|
||||
if (!m_colorRampPipeline)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// We are removing the abort in the case this doesn't build. So give up
|
||||
// drawing if we still don't have a pipeline here.
|
||||
auto pipelineState = m_colorRampPipeline->pipelineState();
|
||||
if (!pipelineState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
MTLRenderPassDescriptor* gradPass =
|
||||
[MTLRenderPassDescriptor renderPassDescriptor];
|
||||
gradPass.renderTargetWidth = kGradTextureWidth;
|
||||
@@ -1196,8 +1236,7 @@ void RenderContextMetalImpl::flush(const FlushDescriptor& desc)
|
||||
0,
|
||||
kGradTextureWidth,
|
||||
static_cast<float>(desc.gradDataHeight))];
|
||||
[gradEncoder
|
||||
setRenderPipelineState:m_colorRampPipeline->pipelineState()];
|
||||
[gradEncoder setRenderPipelineState:pipelineState];
|
||||
[gradEncoder
|
||||
setVertexBuffer:mtl_buffer(flushUniformBufferRing())
|
||||
offset:desc.flushUniformDataOffsetInBytes
|
||||
@@ -1217,6 +1256,20 @@ void RenderContextMetalImpl::flush(const FlushDescriptor& desc)
|
||||
// Tessellate all curves into vertices in the tessellation texture.
|
||||
if (desc.tessVertexSpanCount > 0)
|
||||
{
|
||||
// We failed to load the precompiled library and therefore do not have
|
||||
// the abililty to draw anything.
|
||||
if (!m_tessPipeline)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// We are removing the abort in the case this doesn't build. So give up
|
||||
// drawing if we still don't have a pipeline here.
|
||||
auto pipelineState = m_tessPipeline->pipelineState();
|
||||
if (!pipelineState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MTLRenderPassDescriptor* tessPass =
|
||||
[MTLRenderPassDescriptor renderPassDescriptor];
|
||||
tessPass.renderTargetWidth = kTessTextureWidth;
|
||||
@@ -1230,7 +1283,7 @@ void RenderContextMetalImpl::flush(const FlushDescriptor& desc)
|
||||
[tessEncoder
|
||||
setViewport:make_viewport(
|
||||
0, 0, kTessTextureWidth, desc.tessDataHeight)];
|
||||
[tessEncoder setRenderPipelineState:m_tessPipeline->pipelineState()];
|
||||
[tessEncoder setRenderPipelineState:pipelineState];
|
||||
[tessEncoder setVertexTexture:m_featherTexture
|
||||
atIndex:FEATHER_TEXTURE_IDX];
|
||||
[tessEncoder
|
||||
@@ -1263,6 +1316,26 @@ void RenderContextMetalImpl::flush(const FlushDescriptor& desc)
|
||||
// Render the atlas if we have any offscreen feathers.
|
||||
if ((desc.atlasFillBatchCount | desc.atlasStrokeBatchCount) != 0)
|
||||
{
|
||||
// We failed to load the precompiled library and therefore do not have
|
||||
// the abililty to draw anything.
|
||||
if (!m_atlasStrokePipeline || !m_atlasFillPipeline)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// We are removing the abort in the case this doesn't build. So give up
|
||||
// drawing if we still don't have a pipeline here.
|
||||
auto atlasFillpipelineState = m_atlasFillPipeline->pipelineState();
|
||||
if (!atlasFillpipelineState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto atlasStrokepipelineState = m_atlasStrokePipeline->pipelineState();
|
||||
if (!atlasStrokepipelineState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MTLRenderPassDescriptor* atlasPass =
|
||||
[MTLRenderPassDescriptor renderPassDescriptor];
|
||||
atlasPass.renderTargetWidth = desc.atlasContentWidth;
|
||||
@@ -1323,8 +1396,7 @@ void RenderContextMetalImpl::flush(const FlushDescriptor& desc)
|
||||
|
||||
if (desc.atlasFillBatchCount != 0)
|
||||
{
|
||||
[atlasEncoder
|
||||
setRenderPipelineState:m_atlasFillPipeline->pipelineState()];
|
||||
[atlasEncoder setRenderPipelineState:atlasFillpipelineState];
|
||||
for (size_t i = 0; i < desc.atlasFillBatchCount; ++i)
|
||||
{
|
||||
const gpu::AtlasDrawBatch& fillBatch = desc.atlasFillBatches[i];
|
||||
@@ -1349,8 +1421,7 @@ void RenderContextMetalImpl::flush(const FlushDescriptor& desc)
|
||||
|
||||
if (desc.atlasStrokeBatchCount != 0)
|
||||
{
|
||||
[atlasEncoder
|
||||
setRenderPipelineState:m_atlasStrokePipeline->pipelineState()];
|
||||
[atlasEncoder setRenderPipelineState:atlasStrokepipelineState];
|
||||
for (size_t i = 0; i < desc.atlasStrokeBatchCount; ++i)
|
||||
{
|
||||
const gpu::AtlasDrawBatch& strokeBatch =
|
||||
|
||||
@@ -1033,8 +1033,7 @@ void RenderContext::LogicalFlush::layoutResources(
|
||||
m_flushDesc.clockwiseFillOverride = frameDescriptor.clockwiseFillOverride;
|
||||
m_flushDesc.wireframe = frameDescriptor.wireframe;
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
m_flushDesc.synthesizeCompilationFailures =
|
||||
frameDescriptor.synthesizeCompilationFailures;
|
||||
m_flushDesc.synthesizedFailureType = frameDescriptor.synthesizedFailureType;
|
||||
#endif
|
||||
|
||||
m_flushDesc.externalCommandBuffer = flushResources.externalCommandBuffer;
|
||||
|
||||
@@ -93,8 +93,7 @@ std::unique_ptr<TestingGLRenderer> TestingGLRenderer::Make(
|
||||
.wireframe = options.wireframe,
|
||||
.clockwiseFillOverride =
|
||||
m_backendParams.clockwise || options.clockwiseFillOverride,
|
||||
.synthesizeCompilationFailures =
|
||||
options.synthesizeCompilationFailures,
|
||||
.synthesizedFailureType = options.synthesizedFailureType,
|
||||
};
|
||||
m_renderContext->beginFrame(frameDescriptor);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define TESTING_WINDOW_HPP
|
||||
|
||||
#include "common/offscreen_render_target.hpp"
|
||||
#include "rive/renderer/gpu.hpp"
|
||||
#include "rive/refcnt.hpp"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@@ -157,7 +158,8 @@ public:
|
||||
bool disableRasterOrdering = false;
|
||||
bool wireframe = false;
|
||||
bool clockwiseFillOverride = false;
|
||||
bool synthesizeCompilationFailures = false;
|
||||
rive::gpu::SynthesizedFailureType synthesizedFailureType =
|
||||
rive::gpu::SynthesizedFailureType::none;
|
||||
};
|
||||
virtual std::unique_ptr<rive::Renderer> beginFrame(const FrameOptions&) = 0;
|
||||
virtual void endFrame(std::vector<uint8_t>* pixelData = nullptr) = 0;
|
||||
|
||||
@@ -212,8 +212,7 @@ public:
|
||||
.wireframe = options.wireframe,
|
||||
.clockwiseFillOverride =
|
||||
m_backendParams.clockwise || options.clockwiseFillOverride,
|
||||
.synthesizeCompilationFailures =
|
||||
options.synthesizeCompilationFailures,
|
||||
.synthesizedFailureType = options.synthesizedFailureType,
|
||||
});
|
||||
|
||||
return std::make_unique<RiveRenderer>(m_renderContext.get());
|
||||
|
||||
@@ -476,10 +476,11 @@ public:
|
||||
|
||||
~TestingWindowEGL()
|
||||
{
|
||||
eglMakeCurrent(EGL_NO_DISPLAY,
|
||||
eglMakeCurrent(m_Display,
|
||||
EGL_NO_SURFACE,
|
||||
EGL_NO_SURFACE,
|
||||
EGL_NO_CONTEXT);
|
||||
|
||||
if (m_Context)
|
||||
{
|
||||
eglDestroyContext(m_Display, m_Context);
|
||||
|
||||
@@ -220,8 +220,25 @@ public:
|
||||
nullptr);
|
||||
if (!m_glfwWindow)
|
||||
{
|
||||
glfwTerminate();
|
||||
#ifndef __EMSCRIPTEN__
|
||||
const char* errorDescription;
|
||||
int errorCode = glfwGetError(&errorDescription);
|
||||
fprintf(stderr,
|
||||
"Failed to create GLFW window: %s\n",
|
||||
errorDescription);
|
||||
if (errorCode == GLFW_API_UNAVAILABLE)
|
||||
{
|
||||
// This means that the driver does not support the given API and
|
||||
// we cannot create a window. MakeFiddleContext will detect this
|
||||
// object as non-valid and clean it up and return nullptr.
|
||||
return;
|
||||
}
|
||||
#else
|
||||
// Emscripten doesn't support glfwGetError so print a generic
|
||||
// message.
|
||||
fprintf(stderr, "Failed to create GLFW window.\n");
|
||||
#endif
|
||||
glfwTerminate();
|
||||
abort();
|
||||
}
|
||||
glfwMakeContextCurrent(m_glfwWindow);
|
||||
@@ -363,8 +380,7 @@ public:
|
||||
.clockwiseFillOverride =
|
||||
m_backendParams.clockwise || options.clockwiseFillOverride,
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
.synthesizeCompilationFailures =
|
||||
options.synthesizeCompilationFailures,
|
||||
.synthesizedFailureType = options.synthesizedFailureType,
|
||||
#endif
|
||||
};
|
||||
m_fiddleContext->begin(std::move(frameDescriptor));
|
||||
|
||||
@@ -48,8 +48,7 @@ public:
|
||||
.disableRasterOrdering = options.disableRasterOrdering,
|
||||
.wireframe = options.wireframe,
|
||||
.clockwiseFillOverride = options.clockwiseFillOverride,
|
||||
.synthesizeCompilationFailures =
|
||||
options.synthesizeCompilationFailures,
|
||||
.synthesizedFailureType = options.synthesizedFailureType,
|
||||
};
|
||||
m_renderContext->beginFrame(frameDescriptor);
|
||||
m_flushCommandBuffer = [m_queue commandBuffer];
|
||||
|
||||
@@ -33,8 +33,7 @@ public:
|
||||
.disableRasterOrdering = options.disableRasterOrdering,
|
||||
.wireframe = options.wireframe,
|
||||
.clockwiseFillOverride = options.clockwiseFillOverride,
|
||||
.synthesizeCompilationFailures =
|
||||
options.synthesizeCompilationFailures,
|
||||
.synthesizedFailureType = options.synthesizedFailureType,
|
||||
};
|
||||
m_renderContext->beginFrame(frameDescriptor);
|
||||
return std::make_unique<RiveRenderer>(m_renderContext.get());
|
||||
|
||||
@@ -134,8 +134,7 @@ public:
|
||||
.wireframe = options.wireframe,
|
||||
.clockwiseFillOverride =
|
||||
m_backendParams.clockwise || options.clockwiseFillOverride,
|
||||
.synthesizeCompilationFailures =
|
||||
options.synthesizeCompilationFailures,
|
||||
.synthesizedFailureType = options.synthesizedFailureType,
|
||||
};
|
||||
m_renderContext->beginFrame(frameDescriptor);
|
||||
return std::make_unique<RiveRenderer>(m_renderContext.get());
|
||||
|
||||
@@ -34,9 +34,9 @@ static void execute_tool()
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
tcpCheck = TCPClient::Connect(pngServer);
|
||||
printf("Ensure the device is connected to WiFi, is on the same "
|
||||
NSLog(@"Ensure the device is connected to WiFi, is on the same "
|
||||
"local network as the "
|
||||
"host, and app has local network permissions.\n");
|
||||
"host, and app has local network permissions.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc <= 1)
|
||||
{
|
||||
printf("No arguments supplied.");
|
||||
NSLog(@"No arguments supplied.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,19 +31,44 @@ static std::function<std::unique_ptr<TestingWindow>()>
|
||||
},
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
// TODO: d3d12 currently fails with:
|
||||
// Assertion failed: m_copyFence->GetCompletedValue() == 0, file
|
||||
// C:\...\fiddle_context_d3d12.cpp, line 179
|
||||
// []() {
|
||||
// return rivestd::adopt_unique(TestingWindow::MakeFiddleContext(
|
||||
// TestingWindow::Backend::d3d12,
|
||||
// TestingWindow::Visibility::headless,
|
||||
// {},
|
||||
// nullptr));
|
||||
// },
|
||||
[]() {
|
||||
return rivestd::adopt_unique(TestingWindow::MakeFiddleContext(
|
||||
TestingWindow::Backend::d3d12,
|
||||
{},
|
||||
TestingWindow::Visibility::headless,
|
||||
nullptr));
|
||||
},
|
||||
[]() {
|
||||
return rivestd::adopt_unique(TestingWindow::MakeFiddleContext(
|
||||
TestingWindow::Backend::d3d12,
|
||||
{.atomic = true},
|
||||
TestingWindow::Visibility::headless,
|
||||
nullptr));
|
||||
},
|
||||
[]() {
|
||||
return rivestd::adopt_unique(TestingWindow::MakeFiddleContext(
|
||||
TestingWindow::Backend::d3d,
|
||||
{},
|
||||
TestingWindow::Visibility::headless,
|
||||
nullptr));
|
||||
},
|
||||
[]() {
|
||||
return rivestd::adopt_unique(TestingWindow::MakeFiddleContext(
|
||||
TestingWindow::Backend::d3d,
|
||||
{.atomic = true},
|
||||
TestingWindow::Visibility::headless,
|
||||
nullptr));
|
||||
},
|
||||
[]() {
|
||||
return rivestd::adopt_unique(TestingWindow::MakeFiddleContext(
|
||||
TestingWindow::Backend::gl,
|
||||
{},
|
||||
TestingWindow::Visibility::headless,
|
||||
nullptr));
|
||||
},
|
||||
[]() {
|
||||
return rivestd::adopt_unique(TestingWindow::MakeFiddleContext(
|
||||
TestingWindow::Backend::gl,
|
||||
{.atomic = true},
|
||||
TestingWindow::Visibility::headless,
|
||||
nullptr));
|
||||
@@ -62,57 +87,66 @@ static std::function<std::unique_ptr<TestingWindow>()>
|
||||
// Ensure that rendering still succeeds when compilations fail (e.g., by falling
|
||||
// back on an uber shader or at least not crashing). Valid compilations may fail
|
||||
// in the real world if the device is pressed for resources or in a bad state.
|
||||
TEST_CASE("synthesizeCompilationFailure", "[rendering]")
|
||||
TEST_CASE("synthesizedFailureType", "[rendering]")
|
||||
{
|
||||
for (auto testingWindowFactory : testingWindowFactories)
|
||||
// TODO: There are potentially stronger ways to build some of these
|
||||
// synthesized failures if we were to pass SynthesizedFailureType as a
|
||||
// creation option instead of on beginFrame
|
||||
for (auto failureType : {SynthesizedFailureType::shaderCompilation,
|
||||
SynthesizedFailureType::ubershaderLoad,
|
||||
SynthesizedFailureType::pipelineCreation})
|
||||
{
|
||||
std::unique_ptr<TestingWindow> window = testingWindowFactory();
|
||||
if (window == nullptr)
|
||||
for (auto testingWindowFactory : testingWindowFactories)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Factory* factory = window->factory();
|
||||
std::unique_ptr<TestingWindow> window = testingWindowFactory();
|
||||
if (window == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Factory* factory = window->factory();
|
||||
|
||||
window->resize(32, 32);
|
||||
window->resize(32, 32);
|
||||
|
||||
// Expected colors after we draw a cyan rectangle.
|
||||
std::vector<uint8_t> drawColors;
|
||||
drawColors.reserve(32 * 32 * 4);
|
||||
for (size_t i = 0; i < 32 * 32; ++i)
|
||||
drawColors.insert(drawColors.end(), {0x00, 0xff, 0xff, 0xff});
|
||||
// Expected colors after we draw a cyan rectangle.
|
||||
std::vector<uint8_t> drawColors;
|
||||
drawColors.reserve(32 * 32 * 4);
|
||||
for (size_t i = 0; i < 32 * 32; ++i)
|
||||
drawColors.insert(drawColors.end(), {0x00, 0xff, 0xff, 0xff});
|
||||
|
||||
// Expected colors when only the clear happens (because even the uber
|
||||
// shader failed to compile).
|
||||
std::vector<uint8_t> clearColors;
|
||||
clearColors.reserve(32 * 32 * 4);
|
||||
for (size_t i = 0; i < 32 * 32; ++i)
|
||||
clearColors.insert(clearColors.end(), {0xff, 0x00, 0x00, 0xff});
|
||||
// Expected colors when only the clear happens (because even the
|
||||
// uber shader failed to compile).
|
||||
std::vector<uint8_t> clearColors;
|
||||
clearColors.reserve(32 * 32 * 4);
|
||||
for (size_t i = 0; i < 32 * 32; ++i)
|
||||
clearColors.insert(clearColors.end(), {0xff, 0x00, 0x00, 0xff});
|
||||
|
||||
for (bool disableRasterOrdering : {false, true})
|
||||
{
|
||||
auto renderer = window->beginFrame({
|
||||
.clearColor = 0xffff0000,
|
||||
.doClear = true,
|
||||
.disableRasterOrdering = disableRasterOrdering,
|
||||
.synthesizeCompilationFailures = true,
|
||||
});
|
||||
for (bool disableRasterOrdering : {false, true})
|
||||
{
|
||||
auto renderer = window->beginFrame({
|
||||
.clearColor = 0xffff0000,
|
||||
.doClear = true,
|
||||
.disableRasterOrdering = disableRasterOrdering,
|
||||
.synthesizedFailureType = failureType,
|
||||
});
|
||||
|
||||
rcp<RenderPath> path = factory->makeRenderPath(AABB{0, 0, 32, 32});
|
||||
rcp<RenderPaint> paint = factory->makeRenderPaint();
|
||||
paint->color(0xff00ffff);
|
||||
renderer->drawPath(path.get(), paint.get());
|
||||
rcp<RenderPath> path =
|
||||
factory->makeRenderPath(AABB{0, 0, 32, 32});
|
||||
rcp<RenderPaint> paint = factory->makeRenderPaint();
|
||||
paint->color(0xff00ffff);
|
||||
renderer->drawPath(path.get(), paint.get());
|
||||
|
||||
std::vector<uint8_t> pixels;
|
||||
window->endFrame(&pixels);
|
||||
std::vector<uint8_t> pixels;
|
||||
window->endFrame(&pixels);
|
||||
|
||||
// There are two acceptable results to this test:
|
||||
//
|
||||
// 1) The draw happens anyway because we fell back on a precompiled
|
||||
// uber shader.
|
||||
//
|
||||
// 2) The uber shader also synthesizes a compilation faiulre, so
|
||||
// only the clear color makes it through.
|
||||
CHECK((pixels == drawColors || pixels == clearColors));
|
||||
// There are two acceptable results to this test:
|
||||
//
|
||||
// 1) The draw happens anyway because we fell back on a
|
||||
// precompiled uber shader.
|
||||
//
|
||||
// 2) The uber shader also synthesizes a compilation faiulre, so
|
||||
// only the clear color makes it through.
|
||||
CHECK((pixels == drawColors || pixels == clearColors));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user