Browse Source

scripts can output in linear colour space

Inderjit Gill 1 month ago
parent
commit
8084e1c4a0
6 changed files with 76 additions and 22 deletions
  1. 4
    0
      client/src/lib.rs
  2. 36
    17
      client/www/index.js
  3. 6
    4
      client/www/worker.js
  4. 1
    0
      core/src/context.rs
  5. 3
    1
      core/src/keywords.rs
  6. 26
    0
      core/src/native.rs

+ 4
- 0
client/src/lib.rs View File

@@ -177,6 +177,10 @@ impl Bridge {
177 177
         }
178 178
     }
179 179
 
180
+    pub fn output_linear_colour_space(&self) -> bool {
181
+        self.context.output_linear_colour_space
182
+    }
183
+
180 184
     // --------------------------------------------------------------------------------
181 185
 
182 186
     pub fn get_render_packet_geo_len(&self, packet_number: usize) -> usize {

+ 36
- 17
client/www/index.js View File

@@ -195,6 +195,7 @@ function setupBlitShaders(gl) {
195 195
   varying vec2 vTextureCoord;
196 196
 
197 197
   uniform sampler2D uSampler;
198
+  uniform bool uOutputLinearColourSpace;
198 199
 
199 200
   // https:en.wikipedia.org/wiki/SRGB
200 201
   vec3 linear_to_srgb(vec3 linear) {
@@ -219,7 +220,17 @@ function setupBlitShaders(gl) {
219 220
   void main()
220 221
   {
221 222
      vec4 col = texture2D( uSampler, vTextureCoord );
222
-     gl_FragColor = vec4(linear_to_srgb(col.rgb), 1.0);
223
+
224
+     // note: you _never_ want uOutputLinearColourSpace to be set to true
225
+     // it's only here because some of the older pieces didn't correctly
226
+     // convert from linear colour space to sRGB colour space during rendering
227
+     // and this shader needs to reproduce them as intended at time of creation
228
+     //
229
+     if (uOutputLinearColourSpace) {
230
+       gl_FragColor = col;
231
+     } else {
232
+       gl_FragColor = vec4(linear_to_srgb(col.rgb), 1.0);
233
+     }
223 234
   }
224 235
   `;
225 236
 
@@ -261,6 +272,11 @@ function setupBlitShaders(gl) {
261 272
   shader.mvMatrixUniform = gl.getUniformLocation(shader.program, 'uMVMatrix');
262 273
   shader.textureUniform  = gl.getUniformLocation(shader.program, 'uSampler');
263 274
 
275
+  // older versions of seni (pre 4.2.0) did not convert from sRGB space to linear before blending
276
+  // in order to retain the look of these older pieces we can't carry out the linear -> sRGB conversion
277
+  //
278
+  shader.outputLinearColourSpaceUniform = gl.getUniformLocation(shader.program, 'uOutputLinearColourSpace');
279
+
264 280
   return shader;
265 281
 }
266 282
 
@@ -498,7 +514,7 @@ class GLRenderer {
498 514
 
499 515
   }
500 516
 
501
-  renderTextureToScreen(canvasWidth, canvasHeight) {
517
+  renderTextureToScreen(meta, canvasWidth, canvasHeight) {
502 518
     const gl = this.gl;
503 519
     const domElement = this.glDomElement;
504 520
 
@@ -540,6 +556,9 @@ class GLRenderer {
540 556
 
541 557
     gl.uniform1i(shader.textureUniform, 0);
542 558
 
559
+    gl.uniform1i(shader.outputLinearColourSpaceUniform, meta.output_linear_colour_space);
560
+
561
+
543 562
     const glVertexBuffer = this.glVertexBuffer;
544 563
     const glColourBuffer = this.glColourBuffer;
545 564
     const glTextureBuffer = this.glTextureBuffer;
@@ -680,7 +699,7 @@ function seniMode() {
680 699
 
681 700
   // keywords are core to the seni language
682 701
   const keywords =
683
-        makeKeywords('begin define fn if fence loop on-matrix-stack quote');
702
+        makeKeywords('begin define fn if fence loop on-matrix-stack quote meta');
684 703
   const indentKeys = makeKeywords('define fence loop on-matrix-stack fn');
685 704
 
686 705
   // functions from the common seni library
@@ -1661,7 +1680,7 @@ function updateSelectionUI(state) {
1661 1680
   });
1662 1681
 }
1663 1682
 
1664
-async function renderGeometryBuffers(memory, buffers, imageElement, w, h) {
1683
+async function renderGeometryBuffers(meta, memory, buffers, imageElement, w, h) {
1665 1684
   let destWidth = undefined;
1666 1685
   let destHeight = undefined;
1667 1686
   if (w !== undefined && h !== undefined) {
@@ -1675,14 +1694,14 @@ async function renderGeometryBuffers(memory, buffers, imageElement, w, h) {
1675 1694
   const stopFn = startTiming();
1676 1695
 
1677 1696
   gGLRenderer.renderGeometryToTexture(gConfig.render_texture_width, gConfig.render_texture_height, memory, buffers);
1678
-  gGLRenderer.renderTextureToScreen(destWidth, destHeight);
1697
+  gGLRenderer.renderTextureToScreen(meta, destWidth, destHeight);
1679 1698
 
1680 1699
   await gGLRenderer.copyImageDataTo(imageElement);
1681 1700
 
1682 1701
   stopFn("rendering all buffers");
1683 1702
 }
1684 1703
 
1685
-async function renderGeometryBuffersSection(memory, buffers, imageElement, w, h, section) {
1704
+async function renderGeometryBuffersSection(meta, memory, buffers, imageElement, w, h, section) {
1686 1705
   let destWidth = undefined;
1687 1706
   let destHeight = undefined;
1688 1707
   if (w !== undefined && h !== undefined) {
@@ -1696,7 +1715,7 @@ async function renderGeometryBuffersSection(memory, buffers, imageElement, w, h,
1696 1715
   const stopFn = startTiming();
1697 1716
 
1698 1717
   gGLRenderer.renderGeometryToTexture(gConfig.render_texture_width, gConfig.render_texture_height, memory, buffers, section);
1699
-  gGLRenderer.renderTextureToScreen(destWidth, destHeight);
1718
+  gGLRenderer.renderTextureToScreen(meta, destWidth, destHeight);
1700 1719
 
1701 1720
   await gGLRenderer.copyImageDataTo(imageElement);
1702 1721
 
@@ -1792,13 +1811,13 @@ function normalize_bitmap_url(url) {
1792 1811
 async function renderScript(parameters, imageElement) {
1793 1812
   const stopFn = startTiming();
1794 1813
 
1795
-  let { title, memory, buffers } = await renderJob(parameters);
1796
-  await renderGeometryBuffers(memory, buffers, imageElement);
1814
+  let { meta, memory, buffers } = await renderJob(parameters);
1815
+  await renderGeometryBuffers(meta, memory, buffers, imageElement);
1797 1816
 
1798
-  if (title === '') {
1817
+  if (meta.title === '') {
1799 1818
     stopFn(`renderScript`);
1800 1819
   } else {
1801
-    stopFn(`renderScript-${title}`);
1820
+    stopFn(`renderScript-${meta.title}`);
1802 1821
   }
1803 1822
 }
1804 1823
 
@@ -1946,14 +1965,14 @@ async function renderHighResSection(state, section) {
1946 1965
 
1947 1966
   const stopFn = startTiming();
1948 1967
 
1949
-  const { title, memory, buffers } = await renderJob({
1968
+  const { meta, memory, buffers } = await renderJob({
1950 1969
     script: state.script,
1951 1970
     scriptHash: state.scriptHash,
1952 1971
     genotype: undefined,
1953 1972
   });
1954 1973
   const [width, height] = state.highResolution;
1955
-  await renderGeometryBuffersSection(memory, buffers, image, width, height, section);
1956
-  stopFn(`renderHighResSection-${title}-${section}`);
1974
+  await renderGeometryBuffersSection(meta, memory, buffers, image, width, height, section);
1975
+  stopFn(`renderHighResSection-${meta.title}-${section}`);
1957 1976
   image.classList.remove('hidden');
1958 1977
   loader.classList.add('hidden');
1959 1978
 }
@@ -2242,7 +2261,7 @@ function setupUI(controller) {
2242 2261
 
2243 2262
     const stopFn = startTiming();
2244 2263
 
2245
-    const { title, memory, buffers } = await renderJob({
2264
+    const { meta, memory, buffers } = await renderJob({
2246 2265
       script: state.script,
2247 2266
       scriptHash: state.scriptHash,
2248 2267
       genotype: state.genotype,
@@ -2250,9 +2269,9 @@ function setupUI(controller) {
2250 2269
 
2251 2270
     const [width, height] = [image_resolution, image_resolution];
2252 2271
 
2253
-    await renderGeometryBuffers(memory, buffers, image, width, height);
2272
+    await renderGeometryBuffers(meta, memory, buffers, image, width, height);
2254 2273
 
2255
-    stopFn(`renderHighRes-${title}`);
2274
+    stopFn(`renderHighRes-${meta.title}`);
2256 2275
 
2257 2276
     loader.classList.add('hidden');
2258 2277
 

+ 6
- 4
client/www/worker.js View File

@@ -75,9 +75,12 @@ function renderPackets({  }) {
75 75
     }
76 76
   }
77 77
 
78
-  gState.bridge.script_cleanup();
78
+  const meta = {
79
+    title: '',
80
+    output_linear_colour_space: gState.bridge.output_linear_colour_space()
81
+  };
79 82
 
80
-  const title = '';
83
+  gState.bridge.script_cleanup();
81 84
 
82 85
   // make a copy of the wasm memory
83 86
   //
@@ -88,11 +91,10 @@ function renderPackets({  }) {
88 91
   // WTF note: Expected a perfomance cost in Chrome due to the slice operation
89 92
   // but it seemed to either have no effect or to make the rendering faster!
90 93
   //
91
-
92 94
   const wasmMemory = gState.memory.buffer;
93 95
   const memory = wasmMemory.slice();
94 96
 
95
-  return [{}, { title, memory, buffers }];
97
+  return [{}, { meta, memory, buffers }];
96 98
 }
97 99
 
98 100
 

+ 1
- 0
core/src/context.rs View File

@@ -32,6 +32,7 @@ pub struct Context {
32 32
     pub mappings: Mappings,
33 33
     pub geometry: geometry::Geometry,
34 34
     pub bitmap_cache: BitmapCache,
35
+    pub output_linear_colour_space: bool, // derive Default sets bool to false
35 36
 }
36 37
 
37 38
 impl Context {

+ 3
- 1
core/src/keywords.rs View File

@@ -328,6 +328,8 @@ pub enum Keyword {
328 328
     InnerWidth,
329 329
     #[strum(serialize = "iterations")]
330 330
     Iterations,
331
+    #[strum(serialize = "linear-colour-space")]
332
+    LinearColourSpace,
331 333
     #[strum(serialize = "line-width")]
332 334
     LineWidth,
333 335
     #[strum(serialize = "line-width-end")]
@@ -439,7 +441,7 @@ mod tests {
439 441
     fn test_keyword_enums() {
440 442
         assert_eq!(Keyword::False as i32, 128);
441 443
         assert_eq!(Keyword::True as i32, 129);
442
-        assert_eq!(Keyword::Width as i32, 299);
444
+        assert_eq!(Keyword::Width as i32, 300);
443 445
     }
444 446
 
445 447
     #[test]

+ 26
- 0
core/src/native.rs View File

@@ -57,6 +57,8 @@ pub enum Native {
57 57
     VectorLength,
58 58
     #[strum(serialize = "probe")]
59 59
     Probe,
60
+    #[strum(serialize = "meta")]
61
+    Meta,
60 62
 
61 63
     // shapes
62 64
     //
@@ -314,6 +316,7 @@ pub fn parameter_info(native: Native) -> Result<(Vec<(Keyword, Var)>, i32)> {
314 316
         Native::Nth => nth_parameter_info(),
315 317
         Native::VectorLength => vector_length_parameter_info(),
316 318
         Native::Probe => probe_parameter_info(),
319
+        Native::Meta => meta_parameter_info(),
317 320
         // shapes
318 321
         Native::Line => line_parameter_info(),
319 322
         Native::Rect => rect_parameter_info(),
@@ -437,6 +440,7 @@ pub fn execute_native(
437 440
         Native::Nth => nth_execute(vm),
438 441
         Native::VectorLength => vector_length_execute(vm),
439 442
         Native::Probe => probe_execute(vm, context),
443
+        Native::Meta => meta_execute(vm, context),
440 444
         // shapes
441 445
         Native::Line => line_execute(vm, context),
442 446
         Native::Rect => rect_execute(vm, context),
@@ -779,6 +783,28 @@ fn probe_execute(vm: &mut Vm, context: &mut Context) -> Result<Option<Var>> {
779 783
     Ok(None)
780 784
 }
781 785
 
786
+fn meta_parameter_info() -> Result<(Vec<(Keyword, Var)>, i32)> {
787
+    Ok((
788
+        // input arguments
789
+        vec![
790
+            (Keyword::LinearColourSpace, Var::Float(0.0)),
791
+        ],
792
+        // stack offset
793
+        0,
794
+    ))
795
+}
796
+
797
+fn meta_execute(vm: &mut Vm, context: &mut Context) -> Result<Option<Var>> {
798
+    let default_mask: i32 = vm.stack_peek(2)?;
799
+
800
+    if is_arg_given(default_mask, 1) {
801
+        let scalar: f32 = vm.stack_peek(1)?;
802
+        context.output_linear_colour_space = scalar > 0.0;
803
+    }
804
+
805
+    Ok(None)
806
+}
807
+
782 808
 fn line_parameter_info() -> Result<(Vec<(Keyword, Var)>, i32)> {
783 809
     Ok((
784 810
         // input arguments

Loading…
Cancel
Save