Closed leiradel closed 3 weeks ago
The validation layer errors say that the pipeline pixel formats don't match the render pass / sgl-context pixel formats you're using that pipeline to render to:
desc.depth.pixel_format = d.environment.defaults.depth_format;
desc.colors[0].pixel_format = d.environment.defaults.color_format;
E.g. the environment-default values would only work when using that pipeline for rendering into the swapchain framebuffer, not into your offscreen render target.
For rendering into your offscreen render target you'd probably want these value (taken from your example code here when initializing the 'offscreen context'):
desc.depth.pixel_format = SG_PIXELFORMAT_RGBA8;
desc.colors[0].pixel_format = SG_PIXELFORMAT_NONE;
desc.sample_count = 1;
Thanks. I'm now creating two pipelines:
{
sg_environment_defaults const defaults = sg_query_desc().environment.defaults;
sg_pipeline_desc desc = {};
desc.depth.pixel_format = defaults.depth_format;
desc.colors[0].pixel_format = defaults.color_format;
desc.colors[0].blend.enabled = true;
desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA;
desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
desc.colors[0].blend.op_rgb = SG_BLENDOP_ADD;
desc.colors[0].blend.src_factor_alpha = SG_BLENDFACTOR_ONE;
desc.colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
desc.colors[0].blend.op_alpha = SG_BLENDOP_ADD;
desc.color_count = 1;
desc.sample_count = defaults.sample_count;
s_defaultPipeline = sgl_make_pipeline(&desc);
}
{
sg_pipeline_desc desc = {};
desc.depth.pixel_format = SG_PIXELFORMAT_NONE;
desc.colors[0].pixel_format = SG_PIXELFORMAT_RGBA8;
desc.colors[0].blend.enabled = true;
desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA;
desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
desc.colors[0].blend.op_rgb = SG_BLENDOP_ADD;
desc.colors[0].blend.src_factor_alpha = SG_BLENDFACTOR_ONE;
desc.colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
desc.colors[0].blend.op_alpha = SG_BLENDOP_ADD;
desc.color_count = 1;
desc.sample_count = 1;
s_rtPipeline = sgl_make_pipeline(&desc);
}
I'm using the appropriate one when I start a pass:
void rm::renderer::beginPass(RenderTarget const* target) {
if (target == nullptr) {
sgl_set_context(SGL_DEFAULT_CONTEXT);
sgl_defaults();
sgl_load_pipeline(s_defaultPipeline);
sgl_ortho(0.0f, (float)s_size.width(), (float)s_size.height(), 0.0f, -1.0f, 1.0f);
}
else {
auto const t = (SokolRenderTarget const*)target;
sgl_set_context(t->_context);
sgl_defaults();
sgl_load_pipeline(s_rtPipeline);
sgl_ortho(0.0f, (float)target->size().width(), (float)target->size().height(), 0.0f, -1.0f, 1.0f);
}
}
But I'm still getting the same error:
[ERROR] D:\git\RetroplayerWindows\Retroplayer\ThirdParty\sokol\sokol_gfx.h:16783: VALIDATE_APIP_COLOR_FORMAT: sg_apply_pipeline: pipeline color attachment pixel format doesn't match pass color attachment pixel format
[ERROR] D:\git\RetroplayerWindows\Retroplayer\ThirdParty\sokol\sokol_gfx.h:16790: VALIDATE_APIP_DEPTH_FORMAT: sg_apply_pipeline: pipeline depth pixel_format doesn't match pass depth attachment pixel format
[FATAL] D:\git\RetroplayerWindows\Retroplayer\ThirdParty\sokol\sokol_gfx.h:16168: VALIDATION_FAILED: validation layer checks failed
I appreciate your patience, I can translate VU1 machine code to C that uses SIMD, but I struggle with graphics programming.
Hmm... what does your actual drawing code look like (e.g. where you call sgl_draw()
inside a sokol-gfx sg_begin/end_pass
)?
...just to make sure that there's no mixup between sokol-gfx render passes and sokol-gl contexts.
(tbh: the idea of 'contexts' in the higher level sokol headers is quite confusing and I should come up with something more intuitive which better matches the sokol-gfx api)
This is how I setup everything:
void rm::renderer::init() {
RM_INFO("Initializing renderer");
{
sg_desc desc = {};
desc.environment = sglue_environment();
desc.logger.func = logger;
sg_setup(&desc);
if (!sg_isvalid()) {
RM_FATAL("Failed to setup Sokol GFX");
return;
}
}
{
sgl_desc_t desc = {};
desc.logger.func = logger;
sgl_setup(&desc);
}
{
sg_environment_defaults const defaults = sg_query_desc().environment.defaults;
sg_pipeline_desc desc = {};
desc.depth.pixel_format = defaults.depth_format;
desc.colors[0].pixel_format = defaults.color_format;
desc.colors[0].blend.enabled = true;
desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA;
desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
desc.colors[0].blend.op_rgb = SG_BLENDOP_ADD;
desc.colors[0].blend.src_factor_alpha = SG_BLENDFACTOR_ONE;
desc.colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
desc.colors[0].blend.op_alpha = SG_BLENDOP_ADD;
desc.color_count = 1;
desc.sample_count = defaults.sample_count;
s_defaultPipeline = sgl_make_pipeline(&desc);
}
{
sg_pipeline_desc desc = {};
desc.depth.pixel_format = SG_PIXELFORMAT_NONE;
desc.colors[0].pixel_format = SG_PIXELFORMAT_RGBA8;
desc.colors[0].blend.enabled = true;
desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA;
desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
desc.colors[0].blend.op_rgb = SG_BLENDOP_ADD;
desc.colors[0].blend.src_factor_alpha = SG_BLENDFACTOR_ONE;
desc.colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
desc.colors[0].blend.op_alpha = SG_BLENDOP_ADD;
desc.color_count = 1;
desc.sample_count = 1;
s_rtPipeline = sgl_make_pipeline(&desc);
}
{
sg_sampler_desc desc = {};
desc.min_filter = desc.mag_filter = SG_FILTER_NEAREST;
desc.wrap_u = desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE;
s_samplerNearest = sg_make_sampler(&desc);
if (sg_query_sampler_state(s_samplerNearest) != SG_RESOURCESTATE_VALID) {
RM_FATAL("Failed to create the nearest sampler");
}
}
{
sg_sampler_desc desc = {};
desc.min_filter = desc.mag_filter = SG_FILTER_LINEAR;
desc.wrap_u = desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE;
s_samplerLinear = sg_make_sampler(&desc);
if (sg_query_sampler_state(s_samplerLinear) != SG_RESOURCESTATE_VALID) {
RM_FATAL("Failed to create the linear sampler");
}
}
}
This is the code to begin and end passes:
void rm::renderer::beginPass(RenderTarget const* target) {
if (target == nullptr) {
sgl_set_context(SGL_DEFAULT_CONTEXT);
sgl_defaults();
sgl_load_pipeline(s_defaultPipeline);
sgl_ortho(0.0f, (float)s_size.width(), (float)s_size.height(), 0.0f, -1.0f, 1.0f);
}
else {
auto const t = (SokolRenderTarget const*)target;
sgl_set_context(t->_context);
sgl_defaults();
sgl_load_pipeline(s_rtPipeline);
sgl_ortho(0.0f, (float)target->size().width(), (float)target->size().height(), 0.0f, -1.0f, 1.0f);
}
}
void rm::renderer::endPass(RenderTarget const* target) {
sg_pass pass = {};
pass.action.colors[0].load_action = SG_LOADACTION_CLEAR;
pass.action.colors[0].clear_value = { 0.0f, 0.0f, 0.0f, 1.0f };
if (target == nullptr) {
pass.swapchain = sglue_swapchain();
sg_begin_pass(&pass);
sgl_context_draw(SGL_DEFAULT_CONTEXT);
sg_end_pass();
}
else {
auto const t = (SokolRenderTarget const*)target;
pass.attachments = t->_attachments;
sg_begin_pass(&pass);
sgl_context_draw(t->_context);
sg_end_pass();
}
}
This is how I draw solid quads, textured quads, and quads textured with render targets:
void rm::renderer::draw(rm::Quad const& quad, Post post) {
sg_image image = { SG_INVALID_ID };
draw(quad, image, rm::renderer::Filter::Nearest, post, false);
}
void rm::renderer::draw(rm::Quad const& quad, rm::renderer::Texture const* const texture, Filter const filter, Post const post) {
auto t = (SokolTexture const* const)texture;
draw(quad, t->_texture, filter, post, false);
}
void rm::renderer::draw(rm::Quad const& quad, rm::renderer::RenderTarget const* const target, Filter const filter, Post const post) {
auto t = (SokolRenderTarget const* const)target;
sg_features const features = sg_query_features();
bool const flip_y = !features.origin_top_left;
draw(quad, t->_target, filter, post, flip_y);
}
And this is the actual draw code:
static void draw(rm::Quad const& quad, sg_image image, rm::renderer::Filter filter, rm::renderer::Post post, bool flip_y) {
sg_sampler sampler = (filter == rm::renderer::Filter::Linear) ? s_samplerLinear : s_samplerNearest;
sgl_enable_texture();
sgl_texture(image, sampler);
sgl_begin_quads();
{
rm::Point const& position = quad.position();
rm::Point const& center = quad.center();
rm::Vec2 const& scale = quad.scale();
float const angle = -quad.angle();
rm::Color const& color = quad.color();
rm::Size<float> const& size = quad.size();
rm::Point const screencenter(position.x() + center.x(), position.y() + center.y());
rm::renderer::resetTransforms();
rm::renderer::scaleAt(scale.x(), scale.y(), center.x(), center.y());
rm::renderer::rotateAt(angle, center.x(), center.y());
rm::Point tl = quad.position();
rm::Point tr(tl.x() + size.width(), tl.y());
rm::Point bl(tl.x(), tl.y() + size.height());
rm::Point br(tl.x() + size.width(), tl.y() + size.height());
tl = rm::renderer::transform(tl);
tr = rm::renderer::transform(tr);
bl = rm::renderer::transform(bl);
br = rm::renderer::transform(br);
float const r = color.r();
float const g = color.g();
float const b = color.b();
float const a = color.a();
if (flip_y) {
sgl_v2f_t2f_c4f(tl.x(), tl.y(), 0.0f, 1.0f, r, g, b, a);
sgl_v2f_t2f_c4f(tr.x(), tr.y(), 1.0f, 1.0f, r, g, b, a);
sgl_v2f_t2f_c4f(br.x(), br.y(), 1.0f, 0.0f, r, g, b, a);
sgl_v2f_t2f_c4f(bl.x(), bl.y(), 0.0f, 0.0f, r, g, b, a);
}
else {
sgl_v2f_t2f_c4f(tl.x(), tl.y(), 0.0f, 0.0f, r, g, b, a);
sgl_v2f_t2f_c4f(tr.x(), tr.y(), 1.0f, 0.0f, r, g, b, a);
sgl_v2f_t2f_c4f(br.x(), br.y(), 1.0f, 1.0f, r, g, b, a);
sgl_v2f_t2f_c4f(bl.x(), bl.y(), 0.0f, 1.0f, r, g, b, a);
}
}
sgl_end();
}
Sorry I'm quite distracted with other things ATM. From staring at the code I'm not seeing anything obviously wrong, but this sort of visual remote debugging is also quite hard :)
It might help to set a breakpoint at the sokol_gfx.h validation code line that's printed in the validation errors and then check in the debugger call stack where exactly that error is happening in your code, maybe that pinpoints the problem.
Sure, I appreciate you're doing this during your free time.
I'm not getting any validation errors or any other messages from Sokol, everything just works except there's no alpha blending, my sprites have a black background.
I'll try to write a minimum program that uses sokol_gl this weekend and that reproduces the issue. If it works, I'll probably get enough insight to fix the issue myself, otherwise you'll have a program that you can build and try yourself, time permitting of course.
Thanks for all the help so far!
Oh well, my minimum test program works. I'll add a render target to it and see if it still works.
When I try to render the above images to a render target, I get these errors:
[error] ./sokol_gfx.h:16783: VALIDATE_APIP_COLOR_FORMAT: sg_apply_pipeline: pipeline color attachment pixel format doesn't match pass color attachment pixel format
[error] ./sokol_gfx.h:16790: VALIDATE_APIP_DEPTH_FORMAT: sg_apply_pipeline: pipeline depth pixel_format doesn't match pass depth attachment pixel format
[fatal] ./sokol_gfx.h:16168: VALIDATION_FAILED: validation layer checks failed
[error] ./sokol_gfx.h:16814: VALIDATE_ABND_PIPELINE: sg_apply_bindings: must be called after sg_apply_pipeline
[error] ./sokol_gfx.h:16816: VALIDATE_ABND_PIPELINE_EXISTS: sg_apply_bindings: currently applied pipeline object no longer alive
[fatal] ./sokol_gfx.h:16168: VALIDATION_FAILED: validation layer checks failed
[error] ./sokol_gfx.h:17021: VALIDATE_AUB_NO_PIPELINE: sg_apply_uniforms: must be called after sg_apply_pipeline()
Assertion failed: pip && (pip->slot.id == _sg.cur_pipeline.id), file ./sokol_gfx.h, line 17023
If I comment out the code to render to the render target, but render the render target to the display, it works (but I get a black screen ofc). If I render the images directly to the display, it works.
I'm not sure what isn't matching that is causing the error messages... The program is here, in case you have some time to give it a try.
I'm building it in a Clang MSYS2 prompt with clang -O3 -std=c11 -o test main.c -lgdi32 -lkernel32 -luser32 -ldxgi -ld3d11 -ldinput8 -ldxguid
.
I'll continue trying random stuff on my end and see if I can make it work.
The validation errors about sg_apply_bindings
and sg_apply_uniforms
not being called at the right time might be the actual source of a lot of followup problems.
Is this still purely sokol_gl.h code? Because this type of errors shouldn't happen inside sokol_gl.h, hmm...
I'm currently still a bit overloaded with other stuff, just keep pining this thread from time to time, and I'll find some time to check your example ;)
Ok, on macOS I can build with clang -std=c11 -ObjC -o test main.c -framework Cocoa -framework Metal -framework MetalKit -framework Quartz
(after changing SOKOL_D3D11 to SOKOL_METAL) and get the same validation errors.
I'll try to have a quick look in the debugger...
PS: or rather clang -std=c11 -ObjC -o test main.c -framework Cocoa -framework Metal -framework MetalKit -framework Quartz
to get a debuggable executable...
Aaah, ok, you stumbled over a little detail which I also forgot about...
I found two issues:
The validation errors are because of an admittedly very obscure behavior of sgl_make_pipeline()
. It will ignore the incoming pixel formats and instead patch them with the pixel formats from the current sokol-gl context :/ I have to admit that this is a pretty stupid decision in the sokol-gl API design (I'm not happy with those 'render context' in any of the sokol utility headers and need to come up with something better...).
Long story short, the validation errors can be fixed like this:
sgl_context
handle...sgl_make_pipeline()
for the render target pipeline to use sgl_context_make_pipeline()
instead (and to avoid confusion, it's best to remove all the pixel formats and sample counts from the sgl pipeline creation calls (those will be overwritten inside the pipeline creation call anyway): sg_pipeline_desc desc = {0};
desc.colors[0].blend.enabled = true;
desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA;
desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
desc.colors[0].blend.src_factor_alpha = SG_BLENDFACTOR_ONE;
desc.colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
desc.color_count = 1;
s_rtPipeline = sgl_context_make_pipeline(s_context, &desc)
...I also removed some other items which are default-initialized to the same value anyway.
...this fixes all validation errors, but still doesn't render anything. But this is simply because you forgot to call endDefaultPass()
before sg_commit()
(this should probably also be a sokol-gfx validation error... or at least an assert...).
With those two fixes I get the following output on Metal (don't know yet why the output is only in the top-right corner, the sgl_ortho()
calls look right to me):
This is the main.c file with my fixes:
Btw, feedback like this is very helpful for me to find gaps and weird things which need at least better error checking :)
PS: you'll need to change the SOKOL_METAL
define at the top back to SOKOL_D3D11
(I forgot that).
Woot it works!
Thanks so much, I've made the changes directly in my engine instead of the sample program I wrote and the alpha blending is working just fine. Thanks!
Just one question before closing this issue: is it ok to create one render target context to use with all render targets? Does it create any issue with the rendering order?
Thanks again!
Clarification: I was creating one sgl context per render target, but since it must be created before the pipeline, I'm creating only one and using it for all render targets.
My application only has one render target for now, but could have more, hence the question.
You should create a separate sgl context for each render target, and only do a single sgl_draw()
per context and frame (this is because sgl_draw() will render everything that has been recorded in a specific context in one frame, so doing this multiple times you will end up with rendering the same things multiple times).
The recorded rendering commands in each sgl context are reset at the end of a frame in the sg_commit()
sokol-gfx call.
Ok, I'll have to re-design this part a bit as it's possible to create render targets at will right now. Maybe I'll create a bunch of context at startup and limit the number of simultaneous render targets in use.
Nice, thanks again for all the help!
I need to use blending when drawing sprites, but there no
sgl_enable_blend
function or something similar.I tried to create a pipeline during setup
and then use it when starting passes
but I got a validation error:
Sorry, I'm at a loss here as to how to enable blending.