Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blender changes #2281

Closed

Conversation

standard-two-simplex
Copy link
Contributor

@standard-two-simplex standard-two-simplex commented May 23, 2020

This incorporates changes to the blender to emulate cases where memory color is used in the first cycle of a two-cycle blender and some cases where special alpha is blended.

It needs a fallback if extension GL_EXT_blend_func_extended or GL_EXT_blend_func_separate is not supported by the video card. I would appreciate any help with the design of this and supervision of commit 5e55563.

Any cleanup suggestions are welcome. I will still try to clean up at least blender1 and blender2 shader parts.

Fixes #585 and #728.

@gonetz
Copy link
Owner

gonetz commented Jun 2, 2020

I checked the code. It looks good for me.
I'll add a code for a fallback if the necessary extension is not supported.

@standard-two-simplex
Copy link
Contributor Author

standard-two-simplex commented Jun 2, 2020

I'll add a code for a fallback

Ok thanks.

Also I added a cleanup commit where I refactored the variables into more readable names and corrected the padding and linebreaks.

I wrote GL_GET_PROC_ADR in this line because ASSIGN_PROC_ADR wasn't compiling. I'm not sure what is doing in detail. @gonetz @fzurita can you check if it is correct or should be changed?

@standard-two-simplex standard-two-simplex marked this pull request as ready for review Jun 2, 2020
@fzurita
Copy link
Contributor

fzurita commented Jun 2, 2020

ASSIGN_PROC_ADR is for windows systems if I remember correctly where using GL_GET_PROC_ADR will fail for older OpenGL functions.

GL_GET_PROC_ADR is probably correct, but you may have it in the incorrect section for it to work in all platforms.

@fzurita
Copy link
Contributor

fzurita commented Jun 2, 2020

Yeah, looking closer, you probably want to use GL_GET_PROC_ADR only once and apply it to all platforms.

I would put it right after this line and remove it from the two other places:

GL_GET_PROC_ADR(PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC, EGLImageTargetRenderbufferStorageOES);

@gonetz
Copy link
Owner

gonetz commented Jun 3, 2020

'll add a code for a fallback if the necessary extension is not supported.

Done. The whole PR is in
https://github.com/gonetz/GLideN64/commits/blender_changes
branch. Please check what is missing or not working.

@gonetz
Copy link
Owner

gonetz commented Jun 3, 2020

BTW, glBlendFuncSeparate is core since OpenGL 2.0 and is supported by GLES 2.0.
Do we really need to check that it is supported in drivers?

@loganmc10
Copy link
Contributor

loganmc10 commented Jun 3, 2020

I was just going to say, GL_ARB_blend_func_extended has been core since OpenGL 3.3, which is the minimum required by GLideN64, so there is no need to check for it.
There is also no need to add #extension GL_ARB_blend_func_extended : enable to the shader headers

For OpenGL ES, the extension is GL_EXT_blend_func_extended, which I don't think is core in any version of GLES. @gonetz I don't think the extension is needed because of the use of glBlendFuncSeparate, but rather the use of things like SRC1_COLOR

@loganmc10
Copy link
Contributor

loganmc10 commented Jun 3, 2020

In terms of OpenGL ES support, it looks like the extension is supported on Adreno 5xx and 6xx devices running Android 9 or later, as well as all Nvidia devices (Shield and Pixel C)

@standard-two-simplex
Copy link
Contributor Author

standard-two-simplex commented Jun 3, 2020

@gonetz I don't think the extension is needed because of the use of glBlendFuncSeparate, but rather the use of things like SRC1_COLOR

New blender code needs GL_blend_func extended to use fragColor1 as secondary fragment shader output which is then referred as SRC1_COLOR, and alpha blending needs both GL_blend_func_extended to use SRC1_COLOR and glBlendFuncSeparate() to separate color and alpha equations.

If any of them is core, then I guess there is no need to check for that one.

@loganmc10
Copy link
Contributor

loganmc10 commented Jun 3, 2020

Yes that is all core in OpenGL, but not OpenGL ES

@standard-two-simplex
Copy link
Contributor Author

standard-two-simplex commented Jun 3, 2020

I added some cleanup so that commits so that they can eventually be squashed into two commits, each up to cleanup commit. Feel free to add the fallback commit if we're going to use it.

c9f55c1 will need editing if we are to write a fallback for GLES devices.

I noticed the following problem with the fallback. It is due to 1.0 - dstFactor not being the correct alpha, which is unsolvable without the extension.

So either we have to deal with it in devices that don't support blend_func_extended and possibly use the legacy blender or restore the old code, which would leave three ways of blending: legacy, old and new. I don't know what is better.

@gonetz
Copy link
Owner

gonetz commented Jun 4, 2020

Yes that is all core in OpenGL, but not OpenGL ES

As I see, the glBlendFuncSeparate is core in GLES 2.0, but SRC1_COLOR is not supported by GLES.
http://docs.gl/es2/glBlendFuncSeparate

@gonetz
Copy link
Owner

gonetz commented Jun 4, 2020

three ways of blending: legacy, old and new. I don't know what is better.

Legacy blending is a regression for GLES3 devices. Thus, I think we need to support the all three ways.

@standard-two-simplex could you rewrite the code to fallback into the old blending code? It should be easy: check for config.generalEmulation.enableLegacyBlending and for Context::SeparateBlending and call corresponding blending code.

@standard-two-simplex
Copy link
Contributor Author

standard-two-simplex commented Jun 4, 2020

@standard-two-simplex could you rewrite the code to fallback into the old blending code?

Ok, I will give it a go.

If it is ok, I will create dstFactor as a dummy variable which is populated but unused in the fallback. This way shader code will be almost the same and only GraphicsDrawer::setBlending() will differ considerably.

gonetz and others added 3 commits Jun 4, 2020
@standard-two-simplex
Copy link
Contributor Author

standard-two-simplex commented Jun 4, 2020

Done. It's working on my PC with and without fallback.

@loganmc10 Regarding GLES and GL_EXT_blend_func_extended

  • Does GLES2 also support it? I think I need to write it in GLES2 branch here too. Can you confirm it?

  • Do I need to define tokens like SRC1_COLOR for GL and tokens like SRC1_COLOR_EXT for GL ES?

@loganmc10
Copy link
Contributor

loganmc10 commented Jun 4, 2020

Does GLES2 also support it?

No, GL_EXT_blend_func_extended requires GLES3

Do I need to define tokens like SRC1_COLOR for GL and tokens like SRC1_COLOR_EXT for GL ES?

That might be a question for @fzurita , a lot of the GL backend has been abstracted and I can't really remember how the GL headers are pulled in. But generally no, those things should be defined in the GL headers. You shouldn't be re-defining them

@standard-two-simplex
Copy link
Contributor Author

standard-two-simplex commented Jun 4, 2020

I had to define some of the tokes here.
c9f55c1#diff-d0dca284c9d54e453c4cc1ef068db49fR120

The question was whether I have to define them in a per GL version basis, or if they can be somehow abstracted to work for all versions.

@loganmc10
Copy link
Contributor

loganmc10 commented Jun 4, 2020

oh I understand, well that will definitely work for OpenGL. For GLES I'm not 100% sure, it would depend how the GL headers are pulled in, I would just submit it as-is, when someone goes to build for GLES it'll be pretty obvious what needs to be fixed if there is a problem. It's a bit annoying that they add the _EXT for GLES, but I'm sure they have their reasons

@standard-two-simplex
Copy link
Contributor Author

standard-two-simplex commented Jun 5, 2020

Mmm. If I write GL_SRC1_COLOR_EXT in PC it stops compiling.

The extension pages say the following.
GL_ARB_BLEND_FUNC_EXTENDED

SRC1_COLOR                            0x88F9
SRC1_ALPHA
ONE_MINUS_SRC1_COLOR                  0x88FA
ONE_MINUS_SRC1_ALPHA                  0x88FB

GL_EXT_BLEND_FUNC_EXTENDED

SRC1_COLOR_EXT                     0x88F9
SRC1_ALPHA_EXT                     0x8589  // OpenGL 1.5 token value
ONE_MINUS_SRC1_COLOR_EXT           0x88FA
ONE_MINUS_SRC1_ALPHA_EXT           0x88FB
SRC_ALPHA_SATURATE_EXT             0x0308

If current code fails, perhaps adding

#define SRC1_COLOR 0x88F9
#define SRC1_ALPHA 0x8589
#define ONE_MINUS_SRC1_COLOR 0x88FA
#define ONE_MINUS_SRC1_ALPHA 0x88FB

to some GL ES header solves the incompatibility.

@fzurita Any idea if this should be necessary?

@fzurita
Copy link
Contributor

fzurita commented Jun 5, 2020

Some constants are not defined in all platforms. There is some precedence for defining these constants even though they are not used just to get things to compile. For example:

#define GL_LUMINANCE 0x1909

@gonetz
Copy link
Owner

gonetz commented Jun 6, 2020

Current code looks ok, but GLES 2 is broken again

@standard-two-simplex
Copy link
Contributor Author

standard-two-simplex commented Jun 6, 2020

Shader linking error? Do you know if it is GLES2 only or GLES in general?

I think the issue might be due to the layout declaration in index=0 in

"layout(location = 0, index = 0) OUT lowp vec4 fragColor; \n"

@gonetz
Copy link
Owner

gonetz commented Jun 6, 2020

I'm not sure yet. Probably it is my virtual Android device is broken.

Update: yes, the problem is with the layout declaration.

Update 2, the fix:

		if (_glinfo.dual_source_blending) {
			m_part +=
    			"layout(location = 0, index = 0) OUT lowp vec4 fragColor; 	\n"
				"layout(location = 0, index = 1) OUT lowp vec4 fragColor1;  \n"
				;
		} else
                   m_part += "OUT lowp vec4 fragColor; 	\n";

@gonetz
Copy link
Owner

gonetz commented Jun 6, 2020

I put this PR in https://github.com/gonetz/GLideN64/commits/blender_changes branch.
GLES 2.0 works, but "render 2D in N64 resolution" feature is broken.

@gonetz
Copy link
Owner

gonetz commented Jun 7, 2020

I need help with "render 2D in N64 resolution".
How it works:

  • Render texrect into an auxiliary buffer with blending off.
  • render aux buffer as texture into main buffer. The fragment shader just reads texel from the input texture and writes it to the output color. Blending is on.

Blending works if aux buffer keeps alpha from alpha combiner, as it is with the ordinary blending.

With dual blending output alpha comes from the alpha blender.
I need to read secondary color from the buffer and pass it to the secondary output.
How to read the secondary color?

@standard-two-simplex
Copy link
Contributor Author

standard-two-simplex commented Jun 7, 2020

I need to read secondary color from the buffer and pass it to the secondary output.

I'm not sure what you mean, but I'll explain how dual blending works.

Ordinary blending has one output with RGB and A channels. Let's call them fragColor.rgb and fragColor.a. The variable fragColor is populated by the fragment shader. Then the blender performs equation

fragColor.rgba * SRCFACTOR + memColor.rgba * DSTFACTOR

where SRCFACTOR and DSTFACTOR are defined by glBlendFunc(), where fragColor.a or 1.0 - fragColor.a might be chosen. One typical equation might be

fragColor.rgba * fragColor.a + memColor.rgba * (1.0 - fragColor.a)

but as you may notice, the alpha output is a bit strange.

Dual source blending only extends the range of factors that you may plug into SRCFACTOR and DSTFACTOR. The fragment shader outputs fragColor1.rgb and fragColor1.a too, but the equation is still

fragColor.rgba * SRCFACTOR + memColor.rgba * DSTFACTOR

So for example you may perform equation

fragColor.rgba * fragColor1.a + memColor.rgba * (1.0 - fragColor1.a)

with an alpha (fragColor1.a) that you calculated in the fragment shader.

Finally, glBlendFuncSeparate() allows you to split the previous equation into the following two

fragColor.rgb * SRCFACTORCOLOR + memColor.rgb * DSTFACTORCOLOR
fragColor.a * SRCFACTORALPHA + memColor.a * DSTFACTORALPHA

where you may write a logical alpha output.

@standard-two-simplex
Copy link
Contributor Author

standard-two-simplex commented Jun 7, 2020

By the way, are you checking the issue Olivier reported or do you know of another place where native rects fails?

I have uploaded a local backup branch I had, because code refactoring broke intermediate commits.

I have been checking Olivier's issue report and commit 9d842f8 is already creating the issue, which is before alpha blending. As you can see, that commit only makes the previous code use SRC1_ALPHA instead of ONE_MINUS_SRC_ALPHA.

What puzzles me most, is that whatever value I hack into fragColor1, those rects aren't affected. I believe the issue might be mblender1->write() not being called when native rects setting is enabled.

Edit: mblender1->write() is being called, but I'm still puzzled why fragColor1 doesn't affect output.

@gonetz
Copy link
Owner

gonetz commented Jun 7, 2020

I'm not sure what you mean, but I'll explain how dual blending works.

Thanks for the explanation. Now I'll try to explain, what is the problem with native rects

"render 2D in N64 resolution" has two phases

One. Render texrects into an auxiliary buffer. Blending set off. Thus, the resulted color is the first part of the blending equation: fragColor = fragColor.rgba * SRCFACTOR, because memColor.rgba is set to 0 in the shader.
Two. Render aux buffer as texture into main buffer. The fragment shader for native rects just reads texel from the input texture and writes it to the output color. That is output of that shader is equal to output of the shader from the phase one. Blending is on, so the final output corresponds to the full blending equation fragColor.rgba * SRCFACTOR + memColor.rgba * DSTFACTOR

I think, that this method may work with dual blending too.
Dual blending method expects SRC1_COLOR and SRC1_ALPHA outputs from the fragment shader.
Current shader for native rects has no blending code, it just outputs (filtered) color from the input frame buffer texture. That it it has only one fragColor output. I may add the second output color, but I don't know how to read the data for it from the aux buffer.

@standard-two-simplex
Copy link
Contributor Author

standard-two-simplex commented Jun 7, 2020

Two. Render aux buffer as texture into main buffer. The fragment shader for native rects just reads texel from the input texture and writes it to the output color. That is output of that shader is equal to output of the shader from the phase one. Blending is on, so the final output corresponds to the full blending equation fragColor.rgba * SRCFACTOR + memColor.rgba * DSTFACTOR

So in this step, what do you use as SCRFACTOR and DSTFACTOR? Are they always fragColor.a and 1.0 - fragColor.a? If this is the case you would need to change _dualSourceBlending() to do something like

if (drawing_auxiliary_buffer_with_rect_data) {
  BlendParam srcFactor = blend::SRC_ALPHA;
  BlendParam dstFactor = blend::ONE_MINUS_SRC_ALPHA;
  BlendParam srcFactorAlpha = blend::ONE;
  BlendParam dstFactorAlpha = blend::ZERO;
  gfxContext.enable(enable::BLEND, true);
  gfxContext.setBlendingSeparate(srcFactor, dstFactor, srcFactorAlpha, dstFactorAlpha);
} else {
  // Current code
}

@gonetz
Copy link
Owner

gonetz commented Jun 8, 2020

So in this step, what do you use as SCRFACTOR and DSTFACTOR?

Exactly the same blend mode, which was set to render the texrect.
Using of "render 2D in N64 resolution" should be equivalent to direct rendering of texrects to the main buffer. "render 2D in N64 resolution" renders texrect to a aux buffer using the same shader, as in normal case. Aux buffer serves as a temporal storage for output color. Phase two uses a simple shader, which just reads color from the aux buffer texture and outputs it.

Normal case:
Shader:
combinedColor = ColorCombiner(inputColors)
fragColor = Blender(combinedColor)
OpenGL blender:
fragColor.rgba * SRCFACTOR + memColor.rgba * DSTFACTOR

Native res case:
Phase 1
Shader:
combinedColor = ColorCombiner(inputColors)
fragColor = Blender(combinedColor)
frag color stored in the aux buffer
Phase 2
fragColor = texelFetch()
OpenGL blender:
fragColor.rgba * SRCFACTOR + memColor.rgba * DSTFACTOR

Are they always fragColor.a and 1.0 - fragColor.a?

Obviously no. That is the problem.

@standard-two-simplex
Copy link
Contributor Author

standard-two-simplex commented Jun 8, 2020

Ok, I think I get it now.

With dual source blending the fragment shader outputs two variables, but only one is written to the color buffer. With blending disabled that would be fragColor, so fragColor1 is lost. I don't think it is possible to store both values.

The simplest way to solve the problem would be to move fragColor=Blender(combinedColor) to Phase 2. Notice that calculating it in Phase 1 doesn't hurt, so you might prefer to calculate it in both phases. It would also be possible to recalculate only the values of fragColor1, as the rest you can read from previous phase.

I hope I'm making more sense now.

@gonetz
Copy link
Owner

gonetz commented Jun 8, 2020

Yes, it is clearer now for me, thanks! However, implementing blending in native res shader is not simple at all - it is a special shader, not included into standard update mechanism.
I'm thinking about disabling dual blending in shaders when native res is working, but it is not simple either. I either need to keep two versions of shaders and switch between them, or keep two versions of blender in shaders and switch between blenders with an uniform flag.
I may also totally disable the dual blending when native res texrects enabled. It is the simplest solution, but it is kind of regression.

@standard-two-simplex
Copy link
Contributor Author

standard-two-simplex commented Jun 8, 2020

It should be possible to output the value of fragColor1 if you add an extra color attachement to the auxiliary framebuffer. This way you would have two output textures, one with information about fragColor and one with information about fragColor1.

Then you'd have to modify the fragment shader for phase 1 to bind fragColor1 to the second color attachment (layout(location = N) OUT lowp vec4 fragColor1;) and modify the fragment shader for phase 2 to read from this texture and output the result to index 1 of location 0 (layout(location = 0, index=1) OUT lowp vec4 fragColor1;).

If you feel like you know how to do it, please give it a try. Otherwise, point me to the code where the auxiliary buffer for phase one and the fragment shader for phase 2 are set up, and I'll try to get something, but I'm inexperienced in these regard so I'm not too optimistic.

@gonetz
Copy link
Owner

gonetz commented Jun 9, 2020

Does not it contradict with https://www.khronos.org/opengl/wiki/Blending:
"Note: In virtually every OpenGL implementation and hardware, GL_MAX_DUAL_SOURCE_DRAW_BUFFERS is 1. This applies to 3.x and 4.x hardware alike. For all practical purposes, if you use dual-source blending, you can only write to one buffer."
?

Anyway, it all sounds complex. "you'd have to modify the fragment shader for phase 1 to bind fragColor1 to the second color attachment (layout(location = N) OUT lowp vec4 fragColor1;)" is bad. I need too use two versions of shaders and switch between them. Native res rects code is already very complex and thus is not a bug-proof. Such changes will add new level of complexity, so the code will be hard to maintain. Current version with ordinary blending at least does not require special modifications in shaders.

If there was a way to switch shaders into native-res compatible mode by changing a uniform parameter, it could save the situation. Unfortunately, we can't use

int uniform N;
layout(location = N)

If you want to check native res texrects code yourself, GLideN64\src\TexrectDrawer.cpp is the place. Special shader for native res created in GLideN64\src\Graphics\OpenGLContext\GLSL\glsl_SpecialShadersFactory.cpp

I don't see a good solution yet. Imo, the least evil for now is to disable dual blending when native res texrects are enabled.

@standard-two-simplex
Copy link
Contributor Author

standard-two-simplex commented Jun 10, 2020

Does not it contradict with https://www.khronos.org/opengl/wiki/Blending:
"Note: In virtually every OpenGL implementation and hardware, GL_MAX_DUAL_SOURCE_DRAW_BUFFERS is 1. This applies to 3.x and 4.x hardware alike. For all practical purposes, if you use dual-source blending, you can only write to one buffer."
?

I'm not sure. In theory, the shader from phase one with two attachments wouldn't need dual source blending because blending isn't enabled. Only the shader from phase two needs it, which has only one color attachment.

The obvious downside is that the fragment shader would have to be changed whether it's rendering a rect or not.

Such changes will add new level of complexity, so the code will be hard to maintain.

I don't see a good solution yet. Imo, the least evil for now is to disable dual blending when native res texrects are enabled.

I agree, things would get complicated and hard to mantain, it might be best to disable dual source blending when native texrects are enabled.

The dual source fragment shader is compatible with both modes because it outputs the same fragColor. Therefore, I believe changing the condition to
if (Context::DualSourceBlending && (!renderingTexrect || !nativeTexrectsOn))
in GraphicsDrawer::_setBlendMode() and an analog one in UBlendMode2Cycle::update() should suffice.

@gonetz
Copy link
Owner

gonetz commented Jun 13, 2020

I finally found time to fix the issue with texrects drawer.

The dual source fragment shader is compatible with both modes because it outputs the same fragColor.

Actually it is not, because fragment alpha is changed by ShaderBlenderAlpha
So, it is not enough to disable dual blending in GraphicsDrawer::_setBlendMode().
ShaderBlenderAlpha also should be disabled.
I made it in commit 9cf01fb.
Native res texrects work now.

@standard-two-simplex
Copy link
Contributor Author

standard-two-simplex commented Jun 13, 2020

Actually it is not, because fragment alpha is changed by ShaderBlenderAlpha

For CVG_CLAMP AND CVG_WRAP modes fragColor.a is set to clampedColor.a so it is not changed here. For CVG_FULL it is set to 1.0 which I don't think will hurt.

CVG_SAVE might be problematic as it sets fragment alpha to 0.0 and relies on memory alpha.

@gonetz
Copy link
Owner

gonetz commented Jun 13, 2020

CVG_SAVE might be problematic as it sets fragment alpha to 0.0 and relies on memory alpha.

NINTENDO64 logo in Zelda MM uses CVG_SAVE and the logo is invisible in native texrects mode if alpha blending is enabled.

@gonetz
Copy link
Owner

gonetz commented Jun 14, 2020

I tested this PR on a virtual device with GLES 2.0. It works.
Merged.

@gonetz gonetz closed this Jun 14, 2020
@gonetz
Copy link
Owner

gonetz commented Jun 14, 2020

@standard-two-simplex thanks! Another two ancient problems closed.

@standard-two-simplex
Copy link
Contributor Author

standard-two-simplex commented Jun 14, 2020

You're welcome. And thanks to you for solving the native rects issue!

@standard-two-simplex standard-two-simplex deleted the blender_changes branch Jul 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants