Skip to content

Render with 10 bits per color channel if the target framebuffer has more than 8 - #1079

Open
Coastrnaut wants to merge 2 commits into
projectM-visualizer:masterfrom
Coastrnaut:feature/high-bit-depth-targets
Open

Coastrnaut wants to merge 2 commits into
projectM-visualizer:masterfrom
Coastrnaut:feature/high-bit-depth-targets

Conversation

@Coastrnaut

Copy link
Copy Markdown

Implements #1024 (and the question in #895) the way the issue suggests: the internal render targets follow the color depth of the framebuffer the application passes in.

projectM renders each preset into internal RGBA8 images and draws the result to the target framebuffer at the end, so the frame only has 8 bits per channel even if the target has more.

Change

  • ProjectM::RenderFrame() queries the red size of the target's first color buffer in every frame (GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, on GL_BACK_LEFT/GL_BACK for the default framebuffer) and passes it on in the render context.
  • If it is more than 8 bits, a Milkdrop preset creates its two main images, the flip texture and the blur textures as GL_RGB10_A2. That format is color-renderable in OpenGL 3.3 and OpenGL ES 3.0 and stores values clamped to 0..1, like the 8-bit format, so presets that rely on clamping behave the same. Milkdrop's main image has no alpha channel, so the 2 alpha bits aren't a problem.
  • With an 8-bit target the same textures are created as before.
  • If the depth changes while a preset runs, its images are created again, like after a resize.
  • No new API. The behavior is documented at projectm_opengl_render_frame_fbo().

TextureAttachment, Framebuffer and CopyTexture get a function to change the format of an existing attachment; the format choice is in the new Renderer/ColorFormat.hpp.

Open for discussion: I used 10 bits for every target above 8 bits, including float targets such as GL_RGBA16F. A float format for the internal images would change how presets behave, because values would no longer be clamped.

Results

A test app renders into a framebuffer object with an GL_RGBA8, GL_RGB10_A2 or GL_RGBA16F texture and reads the frame back with 16 bits per channel. The presets write a horizontal gradient from 0 to 0.1 in the composite shader, in the warp shader (so it passes through the main image and the flip texture) or show GetBlur1() of it. Distinct levels in the frame at 1280×720:

Target master this PR
RGBA8, all three presets 26 26 (same frames)
RGB10_A2 / RGBA16F, composite shader 26 103
RGB10_A2 / RGBA16F, main image 26 103
RGB10_A2 / RGBA16F, blur1 26 99

Feedback effects get more precise as well, which changes how some presets look on a 10-bit target. A warp shader with ret = tex2D(sampler_main, uv).xyz * 0.98 + 0.004 should settle at 0.2: with 8 bits it stops at 0.098, with 10 bits at 0.175.

Testing

Windows, RTX A6000.

  • 8-bit target, GL 3.3 core: I loaded all 14,536 presets from cream-of-the-crop, en-d, milkdrop-original and projectm-classic one after another, with the preset folders as texture search path, rendered 30 frames each with fixed audio, frame times and random seeds (the seeds were a local patch only, identical on both sides), and recorded shader compile/link results, failed loads and frame hashes. The results are byte-identical to master for every preset.
  • 10-bit target, GL 3.3 core: 20 presets spread over the packs render without GL errors, also when drawing to the default framebuffer.
  • 10-bit target, OpenGL ES context (GLES build): the first 400 presets of the run above render without GL errors, and with the same compile/link results and failed loads as with an 8-bit target.
  • Unit tests pass. There's no new unit test because the test suite doesn't create a GL context.
  • I couldn't look at the result on a 10-bit display; the numbers above are read back from the framebuffer.

It merges cleanly with my other open PRs and with #1056. It conflicts with #1047 in BlurTexture.cpp and ProjectM.hpp, where both add code next to each other.

…ore than 8

projectM renders presets into internal RGBA8 images, so the frame only
had 8 bits per channel even if the application's framebuffer has more.

The number of red bits of the framebuffer the frame is drawn to is now
queried in each frame. If it is more than 8, the main images of a
preset, its flip texture and the blur textures are created as
GL_RGB10_A2, which is color-renderable in OpenGL 3.3 and OpenGL ES 3.0
and, like the 8-bit format, clamps values to 0..1. With an 8-bit
framebuffer, nothing changes.

This branch has not been deployed

No deployments
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.

1 participant