2
0
Эх сурвалжийг харах

reverted approach and added TextureImage instead

codex 8 сар өмнө
parent
commit
819422218f

+ 12 - 3
jme3-core/src/main/java/com/jme3/material/Material.java

@@ -50,6 +50,7 @@ import com.jme3.shader.*;
 import com.jme3.shader.bufferobject.BufferObject;
 import com.jme3.texture.Image;
 import com.jme3.texture.Texture;
+import com.jme3.texture.TextureImage;
 import com.jme3.texture.image.ColorSpace;
 import com.jme3.util.ListMap;
 import com.jme3.util.SafeArrayList;
@@ -451,7 +452,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         }
         return null;
     }
-
+    
     /**
      * Returns a collection of all parameters set on this material.
      *
@@ -514,6 +515,10 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
             if (technique != null) {
                 technique.notifyParamChanged(name, type, value);
             }
+            if (type.isImageType()) {
+                // recompute sort id
+                sortingId = -1;
+            }
         }
     }
 
@@ -859,9 +864,13 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
             Uniform uniform = shader.getUniform(param.getPrefixedName());
             if (!override && uniform.isSetByCurrentMaterial()) return;
 
-            if (type.isTextureType()) {
+            if (type.isTextureType() || type.isImageType()) {
                 try {
-                    renderer.setTexture(unit.textureUnit, (Texture) param.getValue());
+                    if (type.isTextureType()) {
+                        renderer.setTexture(unit.textureUnit, (Texture) param.getValue());
+                    } else {
+                        renderer.setTextureImage(unit.textureUnit, (TextureImage) param.getValue());
+                    }
                 } catch (TextureUnitException exception) {
                     int numTexParams = unit.textureUnit + 1;
                     String message = "Too many texture parameters (" + numTexParams + ") assigned\n to " + toString();

+ 10 - 0
jme3-core/src/main/java/com/jme3/renderer/Renderer.java

@@ -42,6 +42,7 @@ import com.jme3.system.AppSettings;
 import com.jme3.texture.FrameBuffer;
 import com.jme3.texture.Image;
 import com.jme3.texture.Texture;
+import com.jme3.texture.TextureImage;
 import com.jme3.util.NativeObject;
 import java.nio.ByteBuffer;
 import java.util.EnumMap;
@@ -273,6 +274,15 @@ public interface Renderer {
      */
     public void setTexture(int unit, Texture tex)
             throws TextureUnitException;
+    
+    /**
+     * Assigns a TextureImage to the specified texture unit.
+     * 
+     * @param unit the index of the texture unit (≥0)
+     * @param tex the texture image to assign
+     * @throws TextureUnitException if the texture unit does not exist
+     */
+    public void setTextureImage(int unit, TextureImage tex) throws TextureUnitException;
 
     /**
      * Modifies the given Texture with the given Image.

+ 15 - 15
jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java

@@ -61,6 +61,7 @@ import com.jme3.texture.Texture;
 import com.jme3.texture.Texture2D;
 import com.jme3.texture.Texture.ShadowCompareMode;
 import com.jme3.texture.Texture.WrapAxis;
+import com.jme3.texture.TextureImage;
 import com.jme3.texture.image.LastTextureState;
 import com.jme3.util.BufferUtils;
 import com.jme3.util.ListMap;
@@ -2549,13 +2550,6 @@ public final class GLRenderer implements Renderer {
         }
         if (context.boundTextures[unit]==null||context.boundTextures[unit].get() != img.getWeakRef().get()) {
             gl.glBindTexture(target, img.getId());
-            if (gl4 != null && img.getAccess() != null) {
-                // binds the image so that imageStore and imageLoad operations
-                // can be used in shaders on the image
-                gl4.glBindImageTexture(unit, img.getId(), 0, img.isLayered(),
-                        Math.max(img.getBindLayer(), 0), img.getAccess().getGlEnum(),
-                        texUtil.getImageFormat(img.getFormat(), false).internalFormat);
-            }
             context.boundTextures[unit] = img.getWeakRef();
             statistics.onTextureUse(img, true);
         } else {
@@ -2578,13 +2572,6 @@ public final class GLRenderer implements Renderer {
                 context.boundTextureUnit = unit;
             }
             gl.glBindTexture(target, img.getId());
-            if (gl4 != null && img.getAccess() != null) {
-                // binds the image so that imageStore and imageLoad operations
-                // can be used in shaders on the image
-                gl4.glBindImageTexture(unit, img.getId(), 0, img.isLayered(),
-                        Math.max(img.getBindLayer(), 0), img.getAccess().getGlEnum(),
-                        texUtil.getImageFormat(img.getFormat(), false).internalFormat);
-            }
             context.boundTextures[unit] = img.getWeakRef();
             statistics.onTextureUse(img, true);
         } else {
@@ -2766,7 +2753,20 @@ public final class GLRenderer implements Renderer {
             if (tex.getName() != null) glext.glObjectLabel(GL.GL_TEXTURE, tex.getImage().getId(), tex.getName());
         }
     }
-
+    
+    @Override
+    public void setTextureImage(int unit, TextureImage tex) throws TextureUnitException {
+        if (unit < 0 || unit >= RenderContext.maxTextureUnits) {
+            throw new TextureUnitException();
+        }
+        WeakReference<Image> ref = context.boundTextures[unit];
+        boolean bindRequired = tex.clearUpdateNeeded() || ref == null || ref.get() != tex.getImage().getWeakRef().get();
+        setTexture(unit, tex.getTexture());
+        if (gl4 != null && bindRequired) {
+            tex.bindImage(gl4, texUtil, unit);
+        }
+    }
+    
     @Override
     public void setUniformBufferObject(int bindingPoint, BufferObject bufferObject) {
         if (bufferObject.isUpdateNeeded()) {

+ 2 - 2
jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2021 jMonkeyEngine
+ * Copyright (c) 2009-2024 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -46,7 +46,7 @@ import java.util.logging.Logger;
  * 
  * @author Kirill Vainer
  */
-final class TextureUtil {
+public final class TextureUtil {
 
     private static final Logger logger = Logger.getLogger(TextureUtil.class.getName());
 

+ 18 - 3
jme3-core/src/main/java/com/jme3/shader/VarType.java

@@ -43,9 +43,10 @@ import com.jme3.texture.Texture2D;
 import com.jme3.texture.Texture3D;
 import com.jme3.texture.TextureArray;
 import com.jme3.texture.TextureCubeMap;
+import com.jme3.texture.TextureImage;
 
 public enum VarType {
-
+    
     Float("float", float.class, Float.class),
     Vector2("vec2", Vector2f.class),
     Vector3("vec3", Vector3f.class),
@@ -56,7 +57,8 @@ public enum VarType {
     Vector2Array(true, false, "vec2", Vector2f[].class),
     Vector3Array(true, false, "vec3", Vector3f[].class),
     Vector4Array(true, false, "vec4", Vector4f[].class),
-
+    
+    Int("int", int.class, Integer.class),
     Boolean("bool", Boolean.class, boolean.class),
 
     Matrix3(true, false, "mat3", Matrix3f.class),
@@ -70,12 +72,16 @@ public enum VarType {
     Texture3D(false, true, "sampler3D", Texture3D.class, Texture.class),
     TextureArray(false, true, "sampler2DArray|sampler2DArrayShadow", TextureArray.class, Texture.class),
     TextureCubeMap(false, true, "samplerCube", TextureCubeMap.class, Texture.class),
-    Int("int", int.class, Integer.class),
+    
+    Image2D(false, false, true, "image2D", TextureImage.class),
+    Image3D(false, false, true, "image3D", TextureImage.class),
+    
     UniformBufferObject(false, false, "custom", BufferObject.class),
     ShaderStorageBufferObject(false, false, "custom", BufferObject.class);
 
     private boolean usesMultiData = false;
     private boolean textureType = false;
+    private boolean imageType = false;
     private final String glslType;
     private Class<?>[] javaTypes;
 
@@ -98,6 +104,11 @@ public enum VarType {
             this.javaTypes = new Class<?>[0];
         }
     }
+    
+    VarType(boolean multiData, boolean textureType, boolean imageType, String glslType, Class<?>... javaTypes) {
+        this(multiData, textureType, glslType, javaTypes);
+        this.imageType = imageType;
+    }
 
     /**
      * Check if the passed object is of a type mapped to this VarType
@@ -126,6 +137,10 @@ public enum VarType {
     public boolean isTextureType() {
         return textureType;
     }
+    
+    public boolean isImageType() {
+        return imageType;
+    }
 
     public boolean usesMultiData() {
         return usesMultiData;

+ 7 - 0
jme3-core/src/main/java/com/jme3/system/NullRenderer.java

@@ -48,6 +48,7 @@ import com.jme3.shader.bufferobject.BufferObject;
 import com.jme3.texture.FrameBuffer;
 import com.jme3.texture.Image;
 import com.jme3.texture.Texture;
+import com.jme3.texture.TextureImage;
 
 import java.nio.ByteBuffer;
 import java.util.EnumMap;
@@ -170,6 +171,11 @@ public class NullRenderer implements Renderer {
     public void setTexture(int unit, Texture tex) throws TextureUnitException {
         // do nothing
     }
+    
+    @Override
+    public void setTextureImage(int unit, TextureImage tex) throws TextureUnitException {
+        // do nothing
+    }
 
     @Override
     public void modifyTexture(Texture tex, Image pixels, int x, int y) {
@@ -314,4 +320,5 @@ public class NullRenderer implements Renderer {
     public void setUniformBufferObject(int bindingPoint, BufferObject bufferObject) {
 
     }
+    
 }

+ 0 - 126
jme3-core/src/main/java/com/jme3/texture/Image.java

@@ -39,7 +39,6 @@ import com.jme3.export.Savable;
 import com.jme3.math.FastMath;
 import com.jme3.renderer.Caps;
 import com.jme3.renderer.Renderer;
-import com.jme3.renderer.opengl.GL2;
 import com.jme3.texture.image.ColorSpace;
 import com.jme3.texture.image.LastTextureState;
 import com.jme3.util.BufferUtils;
@@ -598,61 +597,6 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
 
 
 
-    }
-    
-    public enum Access {
-        
-        /**
-         * The image can only read from in a shader.
-         */
-        ReadOnly(true, false, GL2.GL_READ_ONLY),
-        
-        /**
-         * The image can written to in a shader.
-         */
-        WriteOnly(false, true, GL2.GL_WRITE_ONLY),
-        
-        /**
-         * The image can both be written to and read from in a shader.
-         */
-        ReadWrite(true, true, GL2.GL_READ_WRITE);
-        
-        private final boolean read, write;
-        private final int glEnum;
-
-        private Access(boolean read, boolean write, int glEnum) {
-            this.read = read;
-            this.write = write;
-            this.glEnum = glEnum;
-        }
-        
-        /**
-         * If true, the image can be read from in a shader.
-         * 
-         * @return 
-         */
-        public boolean isRead() {
-            return read;
-        }
-
-        /**
-         * If true, the image can be written to in a shader.
-         * 
-         * @return 
-         */
-        public boolean isWrite() {
-            return write;
-        }
-        
-        /**
-         * Corresponding OpenGL enum.
-         * 
-         * @return 
-         */
-        public int getGlEnum() {
-            return glEnum;
-        }
-        
     }
 
     // image attributes
@@ -662,8 +606,6 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
     protected ArrayList<ByteBuffer> data;
     protected int multiSamples = 1;
     protected ColorSpace colorSpace = null;
-    protected Access access = null;
-    protected int bindLayer = -1;
 //    protected int mipOffset = 0;
     
     // attributes relating to GL object
@@ -1268,70 +1210,6 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
         return colorSpace;
     }
 
-    /**
-     * Sets the access modifier for this image.
-     * <p>
-     * If not null, the image will be bound in such a way as to allow
-     * {@code imageStore} and {@code imageLoad} functions to work. Otherwise
-     * the image will be bound normally.
-     * <p>
-     * default=null
-     * 
-     * @param access 
-     */
-    public void setAccess(Access access) {
-        if (this.access != access) {
-            this.access = access;
-            setUpdateNeeded();
-        }
-    }
-    
-    /**
-     * 
-     * @return 
-     * @see #setAccess(com.jme3.texture.Image.Access)
-     */
-    public Access getAccess() {
-        return access;
-    }
-
-    /**
-     * Sets the bind layer used if {@link #getAccess()} does not
-     * return null.
-     * <p>
-     * If greater than or equal to zero, only the specified layer will be
-     * bound. If less than zero, the entire image will be bound.
-     * <p>
-     * default=-1
-     * 
-     * @param bindLayer 
-     */
-    public void setBindLayer(int bindLayer) {
-        if (this.bindLayer != bindLayer) {
-            this.bindLayer = bindLayer;
-            setUpdateNeeded();
-        }
-    }
-    
-    /**
-     * 
-     * @return 
-     * @see #setBindLayer(int) 
-     */
-    public int getBindLayer() {
-        return bindLayer;
-    }
-    
-    /**
-     * Returns true if the entire image will be bound in cases where
-     * {@link #getAccess()} does not return null.
-     * 
-     * @return 
-     */
-    public boolean isLayered() {
-        return bindLayer < 0;
-    }
-
     @Override
     public String toString(){
         StringBuilder sb = new StringBuilder();
@@ -1408,8 +1286,6 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
         capsule.write(multiSamples, "multiSamples", 1);
         capsule.writeByteBufferArrayList(data, "data", null);
         capsule.write(colorSpace, "colorSpace", null);
-        capsule.write(access, "access", null);
-        capsule.write(bindLayer, "bindLayer", -1);
     }
 
     @Override
@@ -1423,8 +1299,6 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
         multiSamples = capsule.readInt("multiSamples", 1);
         data = capsule.readByteBufferArrayList("data", null);
         colorSpace = capsule.readEnum("colorSpace", ColorSpace.class, null);
-        access = capsule.readEnum("access", Access.class, null);
-        bindLayer = capsule.readInt("bindLayer", -1);
         if (mipMapSizes != null) {
             needGeneratedMips = false;
             mipsWereGenerated = true;

+ 308 - 0
jme3-core/src/main/java/com/jme3/texture/TextureImage.java

@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2024 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.texture;
+
+import com.jme3.renderer.opengl.GL2;
+import com.jme3.renderer.opengl.GL4;
+import com.jme3.renderer.opengl.TextureUtil;
+import java.util.Objects;
+
+/**
+ * Wraps a texture so that only a single level of the underlying image is bound
+ * instead of the entire image.
+ * 
+ * @author codex
+ */
+public class TextureImage {
+    
+    /**
+     * Enum specifying the shader access hint of the image.
+     */
+    public enum Access {
+        
+        /**
+         * The image can only read from in a shader.
+         */
+        ReadOnly(true, false, GL2.GL_READ_ONLY),
+        
+        /**
+         * The image can written to in a shader.
+         */
+        WriteOnly(false, true, GL2.GL_WRITE_ONLY),
+        
+        /**
+         * The image can both be written to and read from in a shader.
+         */
+        ReadWrite(true, true, GL2.GL_READ_WRITE);
+        
+        private final boolean read, write;
+        private final int glEnum;
+
+        private Access(boolean read, boolean write, int glEnum) {
+            this.read = read;
+            this.write = write;
+            this.glEnum = glEnum;
+        }
+        
+        /**
+         * If true, the image can be read from in a shader.
+         * 
+         * @return 
+         */
+        public boolean isRead() {
+            return read;
+        }
+
+        /**
+         * If true, the image can be written to in a shader.
+         * 
+         * @return 
+         */
+        public boolean isWrite() {
+            return write;
+        }
+        
+        /**
+         * Corresponding OpenGL enum.
+         * 
+         * @return 
+         */
+        public int getGlEnum() {
+            return glEnum;
+        }
+        
+    }
+    
+    private Texture texture;
+    private int level, layer;
+    private Access access;
+    private boolean updateFlag = true;
+    
+    public TextureImage(Texture texture) {
+        this(texture, 0, -1, Access.ReadWrite);
+    }
+    public TextureImage(Texture texture, Access access) {
+        this(texture, 0, -1, access);
+    }
+    public TextureImage(Texture texture, int level, int layer) {
+        this(texture, level, layer, Access.ReadWrite);
+    }
+    public TextureImage(Texture texture, int level, int layer, Access access) {
+        this.texture = Objects.requireNonNull(texture, "Underlying texture cannot be null");
+        this.level = level;
+        this.layer = layer;
+        this.access = access;
+        if (this.level < 0) {
+            throw new IllegalArgumentException("Level cannot be less than zero.");
+        }
+    }
+    
+    /**
+     * Binds this texture image to the texture unit.
+     * <p>
+     * Calling this is not completely sufficient for totally binding an image
+     * to an image unit. Additionally, the image must be bound beforehand using
+     * {@link GL2#glBindTexture(int, int)}.
+     * 
+     * @param gl4 OpenGL4 implementation (not null)
+     * @param texUtil utility used to convert JME's image format to the corresponding GL enum (not null)
+     * @param unit texture unit to bind to
+     */
+    public void bindImage(GL4 gl4, TextureUtil texUtil, int unit) {
+        Image img = texture.getImage();
+        gl4.glBindImageTexture(unit, img.getId(), level, isLayered(), Math.max(layer, 0),
+                access.getGlEnum(), texUtil.getImageFormat(img.getFormat(), false).internalFormat);
+    }
+    
+    /**
+     * Sets the update flag indicating this texture image needs rebinding.
+     */
+    public void setUpdateNeeded() {
+        updateFlag = true;
+    }
+    
+    /**
+     * Clears the update flag and returns the update flag's value before
+     * it was cleared.
+     * 
+     * @return 
+     */
+    public boolean clearUpdateNeeded() {
+        boolean f = updateFlag;
+        updateFlag = false;
+        return f;
+    }
+    
+    /**
+     * Sets the underlying texture wrapped by this TextureImage.
+     * 
+     * @param texture 
+     */
+    public void setTexture(Texture texture) {
+        Objects.requireNonNull(texture, "Wrapped texture cannot be null.");
+        if (this.texture != texture) {
+            this.texture = texture;
+            updateFlag = true;
+        }
+    }
+    
+    /**
+     * Sets the image level to bind.
+     * <p>
+     * The level controls which mipmap level will be bound to the texture unit,
+     * where zero corresponds to the base level of the texture.
+     * 
+     * @param level level to bind (cannot be negative)
+     */
+    public void setLevel(int level) {
+        if (level < 0) {
+            throw new IllegalArgumentException("Texture image level cannot be negative.");
+        }
+        if (this.level != level) {
+            this.level = level;
+            updateFlag = true;
+        }
+    }
+    
+    /**
+     * Sets the image layer to bind.
+     * <p>
+     * If the underlying texture is a one/two/three demensional array,
+     * cube map, cube map array, two demensional multisample array, then this
+     * specifies which layer of the array to bind.
+     * 
+     * @param layer layer to bind, or negative to bind all layers
+     */
+    public void setLayer(int layer) {
+        if (this.layer != layer && (this.layer >= 0 || layer >= 0)) {
+            this.layer = layer;
+            updateFlag = true;
+        }
+    }
+    
+    /**
+     * Sets the shader access hint with which to bind the image.
+     * 
+     * @param access 
+     */
+    public void setAccess(Access access) {
+        if (this.access != access) {
+            this.access = access;
+            updateFlag = true;
+        }
+    }
+    
+    /**
+     * 
+     * @return 
+     * @see #setTexture(com.jme3.texture.Texture)
+     */
+    public Texture getTexture() {
+        return texture;
+    }
+    
+    /**
+     * Gets the image belonging to the underlying texture.
+     * 
+     * @return 
+     */
+    public Image getImage() {
+        return texture.getImage();
+    }
+    
+    /**
+     * Gets the format of the image belonging to the underlying texture.
+     * 
+     * @return 
+     */
+    public Image.Format getFormat() {
+        return texture.getImage().getFormat();
+    }
+    
+    /**
+     * Gets the native id of the image belonging to the underlying texture.
+     * 
+     * @return 
+     */
+    public int getImageId() {
+        return texture.getImage().getId();
+    }
+    
+    /**
+     * 
+     * @return 
+     * @see #setLevel(int)
+     */
+    public int getLevel() {
+        return level;
+    }
+    
+    /**
+     * 
+     * @return 
+     * @see #setLayer(int)
+     */
+    public int getLayer() {
+        return layer;
+    }
+    
+    /**
+     * 
+     * @return 
+     * @see #setAccess(com.jme3.texture.TextureImage.Access)
+     */
+    public Access getAccess() {
+        return access;
+    }
+    
+    /**
+     * Returns true if all layers of an image will be bound, when
+     * {@code layer} is negative.
+     * 
+     * @return 
+     * @see #setLayer(int)
+     */
+    public boolean isLayered() {
+        return layer < 0;
+    }
+    
+    /**
+     * Returns true if the update flag has been set indicating rebinding
+     * is required.
+     * 
+     * @return 
+     */
+    public boolean isUpdateNeeded() {
+        return updateFlag;
+    }
+    
+}