mirror of
https://github.com/love2d/love.git
synced 2026-08-22 05:25:11 +02:00
Implement Array, Cubemap, and Volume texture types (issue #1111).
- Add love.graphics.newArrayImage, newCubeImage, and newVolumeImage.
- Add love.graphics.newCanvas(w, h, layers) and newCanvas(w, h, layers, settings). Add ‘type’ field to the settings table of newCanvas.
- Add new love.graphics.setCanvas variants: setCanvas(canvas, slice), and setCanvas(canvastable) where canvastable is in the format: {{canvas1, layer=2}, {canvas2, face=5}}
- Add Texture:getTextureType, getDepth, getLayerCount, getMipmapCount, and getFormat.
- Remove Image:getData and Image:refresh.
- Add Image:replacePixels(imagedata [, slice] [, mipmap]).
- Update Canvas:newImageData to accept a slice argument.
- Add love.image.newCubeFaces(imagedata).
--HG--
branch : minor
This commit is contained in:
+2
-4
@@ -539,8 +539,6 @@ set(LOVE_SRC_MODULE_GRAPHICS_OPENGL
|
|||||||
src/modules/graphics/opengl/BufferSync.h
|
src/modules/graphics/opengl/BufferSync.h
|
||||||
src/modules/graphics/opengl/Canvas.cpp
|
src/modules/graphics/opengl/Canvas.cpp
|
||||||
src/modules/graphics/opengl/Canvas.h
|
src/modules/graphics/opengl/Canvas.h
|
||||||
src/modules/graphics/opengl/Font.cpp
|
|
||||||
src/modules/graphics/opengl/Font.h
|
|
||||||
src/modules/graphics/opengl/Graphics.cpp
|
src/modules/graphics/opengl/Graphics.cpp
|
||||||
src/modules/graphics/opengl/Graphics.h
|
src/modules/graphics/opengl/Graphics.h
|
||||||
src/modules/graphics/opengl/Image.cpp
|
src/modules/graphics/opengl/Image.cpp
|
||||||
@@ -559,8 +557,6 @@ set(LOVE_SRC_MODULE_GRAPHICS_OPENGL
|
|||||||
src/modules/graphics/opengl/StreamBuffer.h
|
src/modules/graphics/opengl/StreamBuffer.h
|
||||||
src/modules/graphics/opengl/Text.cpp
|
src/modules/graphics/opengl/Text.cpp
|
||||||
src/modules/graphics/opengl/Text.h
|
src/modules/graphics/opengl/Text.h
|
||||||
src/modules/graphics/opengl/Video.cpp
|
|
||||||
src/modules/graphics/opengl/Video.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(LOVE_SRC_MODULE_GRAPHICS
|
set(LOVE_SRC_MODULE_GRAPHICS
|
||||||
@@ -582,6 +578,8 @@ set(LOVE_SRC_MODULE_IMAGE_ROOT
|
|||||||
src/modules/image/Image.h
|
src/modules/image/Image.h
|
||||||
src/modules/image/ImageData.cpp
|
src/modules/image/ImageData.cpp
|
||||||
src/modules/image/ImageData.h
|
src/modules/image/ImageData.h
|
||||||
|
src/modules/image/ImageDataBase.cpp
|
||||||
|
src/modules/image/ImageDataBase.h
|
||||||
src/modules/image/wrap_CompressedImageData.cpp
|
src/modules/image/wrap_CompressedImageData.cpp
|
||||||
src/modules/image/wrap_CompressedImageData.h
|
src/modules/image/wrap_CompressedImageData.h
|
||||||
src/modules/image/wrap_Image.cpp
|
src/modules/image/wrap_Image.cpp
|
||||||
|
|||||||
@@ -423,9 +423,6 @@
|
|||||||
FA0B7D331A95902C000E1D17 /* Canvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B8D1A95902C000E1D17 /* Canvas.cpp */; };
|
FA0B7D331A95902C000E1D17 /* Canvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B8D1A95902C000E1D17 /* Canvas.cpp */; };
|
||||||
FA0B7D341A95902C000E1D17 /* Canvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B8D1A95902C000E1D17 /* Canvas.cpp */; };
|
FA0B7D341A95902C000E1D17 /* Canvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B8D1A95902C000E1D17 /* Canvas.cpp */; };
|
||||||
FA0B7D351A95902C000E1D17 /* Canvas.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7B8E1A95902C000E1D17 /* Canvas.h */; };
|
FA0B7D351A95902C000E1D17 /* Canvas.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7B8E1A95902C000E1D17 /* Canvas.h */; };
|
||||||
FA0B7D361A95902C000E1D17 /* Font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B8F1A95902C000E1D17 /* Font.cpp */; };
|
|
||||||
FA0B7D371A95902C000E1D17 /* Font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B8F1A95902C000E1D17 /* Font.cpp */; };
|
|
||||||
FA0B7D381A95902C000E1D17 /* Font.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7B901A95902C000E1D17 /* Font.h */; };
|
|
||||||
FA0B7D391A95902C000E1D17 /* Graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B911A95902C000E1D17 /* Graphics.cpp */; };
|
FA0B7D391A95902C000E1D17 /* Graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B911A95902C000E1D17 /* Graphics.cpp */; };
|
||||||
FA0B7D3A1A95902C000E1D17 /* Graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B911A95902C000E1D17 /* Graphics.cpp */; };
|
FA0B7D3A1A95902C000E1D17 /* Graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B911A95902C000E1D17 /* Graphics.cpp */; };
|
||||||
FA0B7D3B1A95902C000E1D17 /* Graphics.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7B921A95902C000E1D17 /* Graphics.h */; };
|
FA0B7D3B1A95902C000E1D17 /* Graphics.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7B921A95902C000E1D17 /* Graphics.h */; };
|
||||||
@@ -860,9 +857,6 @@
|
|||||||
FA27B3C01B4985BF008A9DCE /* wrap_VideoStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA27B3B91B4985BF008A9DCE /* wrap_VideoStream.cpp */; };
|
FA27B3C01B4985BF008A9DCE /* wrap_VideoStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA27B3B91B4985BF008A9DCE /* wrap_VideoStream.cpp */; };
|
||||||
FA27B3C11B4985BF008A9DCE /* wrap_VideoStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA27B3B91B4985BF008A9DCE /* wrap_VideoStream.cpp */; };
|
FA27B3C11B4985BF008A9DCE /* wrap_VideoStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA27B3B91B4985BF008A9DCE /* wrap_VideoStream.cpp */; };
|
||||||
FA27B3C21B4985BF008A9DCE /* wrap_VideoStream.h in Headers */ = {isa = PBXBuildFile; fileRef = FA27B3BA1B4985BF008A9DCE /* wrap_VideoStream.h */; };
|
FA27B3C21B4985BF008A9DCE /* wrap_VideoStream.h in Headers */ = {isa = PBXBuildFile; fileRef = FA27B3BA1B4985BF008A9DCE /* wrap_VideoStream.h */; };
|
||||||
FA27B3C51B4985D8008A9DCE /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA27B3C31B4985D8008A9DCE /* Video.cpp */; };
|
|
||||||
FA27B3C61B4985D8008A9DCE /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA27B3C31B4985D8008A9DCE /* Video.cpp */; };
|
|
||||||
FA27B3C71B4985D8008A9DCE /* Video.h in Headers */ = {isa = PBXBuildFile; fileRef = FA27B3C41B4985D8008A9DCE /* Video.h */; };
|
|
||||||
FA27B3C91B498623008A9DCE /* Theora.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA27B3C81B498623008A9DCE /* Theora.framework */; };
|
FA27B3C91B498623008A9DCE /* Theora.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA27B3C81B498623008A9DCE /* Theora.framework */; };
|
||||||
FA28EBD51E352DB5003446F4 /* BufferSync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA28EBD31E352DB5003446F4 /* BufferSync.cpp */; };
|
FA28EBD51E352DB5003446F4 /* BufferSync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA28EBD31E352DB5003446F4 /* BufferSync.cpp */; };
|
||||||
FA28EBD61E352DB5003446F4 /* BufferSync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA28EBD31E352DB5003446F4 /* BufferSync.cpp */; };
|
FA28EBD61E352DB5003446F4 /* BufferSync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA28EBD31E352DB5003446F4 /* BufferSync.cpp */; };
|
||||||
@@ -996,6 +990,9 @@
|
|||||||
FAC756FA1E4F99D200B91289 /* Effect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAC756F81E4F99D200B91289 /* Effect.cpp */; };
|
FAC756FA1E4F99D200B91289 /* Effect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAC756F81E4F99D200B91289 /* Effect.cpp */; };
|
||||||
FAC756FB1E4F99D200B91289 /* Effect.h in Headers */ = {isa = PBXBuildFile; fileRef = FAC756F91E4F99D200B91289 /* Effect.h */; };
|
FAC756FB1E4F99D200B91289 /* Effect.h in Headers */ = {isa = PBXBuildFile; fileRef = FAC756F91E4F99D200B91289 /* Effect.h */; };
|
||||||
FAC756FC1E4F99DB00B91289 /* Effect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAC756F81E4F99D200B91289 /* Effect.cpp */; };
|
FAC756FC1E4F99DB00B91289 /* Effect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAC756F81E4F99D200B91289 /* Effect.cpp */; };
|
||||||
|
FAD19A171DFF8CA200D5398A /* ImageDataBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAD19A151DFF8CA200D5398A /* ImageDataBase.cpp */; };
|
||||||
|
FAD19A181DFF8CA200D5398A /* ImageDataBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAD19A151DFF8CA200D5398A /* ImageDataBase.cpp */; };
|
||||||
|
FAD19A191DFF8CA200D5398A /* ImageDataBase.h in Headers */ = {isa = PBXBuildFile; fileRef = FAD19A161DFF8CA200D5398A /* ImageDataBase.h */; };
|
||||||
FADF53F81E3C7ACD00012CC0 /* Buffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF53F61E3C7ACD00012CC0 /* Buffer.cpp */; };
|
FADF53F81E3C7ACD00012CC0 /* Buffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF53F61E3C7ACD00012CC0 /* Buffer.cpp */; };
|
||||||
FADF53F91E3C7ACD00012CC0 /* Buffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF53F61E3C7ACD00012CC0 /* Buffer.cpp */; };
|
FADF53F91E3C7ACD00012CC0 /* Buffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF53F61E3C7ACD00012CC0 /* Buffer.cpp */; };
|
||||||
FADF53FA1E3C7ACD00012CC0 /* Buffer.h in Headers */ = {isa = PBXBuildFile; fileRef = FADF53F71E3C7ACD00012CC0 /* Buffer.h */; };
|
FADF53FA1E3C7ACD00012CC0 /* Buffer.h in Headers */ = {isa = PBXBuildFile; fileRef = FADF53F71E3C7ACD00012CC0 /* Buffer.h */; };
|
||||||
@@ -1456,8 +1453,6 @@
|
|||||||
FA0B7B8B1A95902C000E1D17 /* Graphics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Graphics.h; sourceTree = "<group>"; };
|
FA0B7B8B1A95902C000E1D17 /* Graphics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Graphics.h; sourceTree = "<group>"; };
|
||||||
FA0B7B8D1A95902C000E1D17 /* Canvas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Canvas.cpp; sourceTree = "<group>"; };
|
FA0B7B8D1A95902C000E1D17 /* Canvas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Canvas.cpp; sourceTree = "<group>"; };
|
||||||
FA0B7B8E1A95902C000E1D17 /* Canvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Canvas.h; sourceTree = "<group>"; };
|
FA0B7B8E1A95902C000E1D17 /* Canvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Canvas.h; sourceTree = "<group>"; };
|
||||||
FA0B7B8F1A95902C000E1D17 /* Font.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Font.cpp; sourceTree = "<group>"; };
|
|
||||||
FA0B7B901A95902C000E1D17 /* Font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Font.h; sourceTree = "<group>"; };
|
|
||||||
FA0B7B911A95902C000E1D17 /* Graphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = Graphics.cpp; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
|
FA0B7B911A95902C000E1D17 /* Graphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = Graphics.cpp; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
|
||||||
FA0B7B921A95902C000E1D17 /* Graphics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Graphics.h; sourceTree = "<group>"; };
|
FA0B7B921A95902C000E1D17 /* Graphics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Graphics.h; sourceTree = "<group>"; };
|
||||||
FA0B7B931A95902C000E1D17 /* Image.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Image.cpp; sourceTree = "<group>"; };
|
FA0B7B931A95902C000E1D17 /* Image.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Image.cpp; sourceTree = "<group>"; };
|
||||||
@@ -1753,8 +1748,6 @@
|
|||||||
FA27B39C1B498151008A9DCE /* wrap_Video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Video.h; sourceTree = "<group>"; };
|
FA27B39C1B498151008A9DCE /* wrap_Video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Video.h; sourceTree = "<group>"; };
|
||||||
FA27B3B91B4985BF008A9DCE /* wrap_VideoStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_VideoStream.cpp; sourceTree = "<group>"; };
|
FA27B3B91B4985BF008A9DCE /* wrap_VideoStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_VideoStream.cpp; sourceTree = "<group>"; };
|
||||||
FA27B3BA1B4985BF008A9DCE /* wrap_VideoStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_VideoStream.h; sourceTree = "<group>"; };
|
FA27B3BA1B4985BF008A9DCE /* wrap_VideoStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_VideoStream.h; sourceTree = "<group>"; };
|
||||||
FA27B3C31B4985D8008A9DCE /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Video.cpp; sourceTree = "<group>"; };
|
|
||||||
FA27B3C41B4985D8008A9DCE /* Video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Video.h; sourceTree = "<group>"; };
|
|
||||||
FA27B3C81B498623008A9DCE /* Theora.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Theora.framework; path = /Library/Frameworks/Theora.framework; sourceTree = "<absolute>"; };
|
FA27B3C81B498623008A9DCE /* Theora.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Theora.framework; path = /Library/Frameworks/Theora.framework; sourceTree = "<absolute>"; };
|
||||||
FA283EDC1B27CFAA00C70067 /* nogame.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = nogame.lua; sourceTree = "<group>"; };
|
FA283EDC1B27CFAA00C70067 /* nogame.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = nogame.lua; sourceTree = "<group>"; };
|
||||||
FA283EDD1B27CFAA00C70067 /* nogame.lua.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = nogame.lua.h; sourceTree = "<group>"; };
|
FA283EDD1B27CFAA00C70067 /* nogame.lua.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = nogame.lua.h; sourceTree = "<group>"; };
|
||||||
@@ -1847,6 +1840,8 @@
|
|||||||
FAC756F41E4F99B400B91289 /* Effect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Effect.h; sourceTree = "<group>"; };
|
FAC756F41E4F99B400B91289 /* Effect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Effect.h; sourceTree = "<group>"; };
|
||||||
FAC756F81E4F99D200B91289 /* Effect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Effect.cpp; sourceTree = "<group>"; };
|
FAC756F81E4F99D200B91289 /* Effect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Effect.cpp; sourceTree = "<group>"; };
|
||||||
FAC756F91E4F99D200B91289 /* Effect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Effect.h; sourceTree = "<group>"; };
|
FAC756F91E4F99D200B91289 /* Effect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Effect.h; sourceTree = "<group>"; };
|
||||||
|
FAD19A151DFF8CA200D5398A /* ImageDataBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageDataBase.cpp; sourceTree = "<group>"; };
|
||||||
|
FAD19A161DFF8CA200D5398A /* ImageDataBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageDataBase.h; sourceTree = "<group>"; };
|
||||||
FADF53F61E3C7ACD00012CC0 /* Buffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Buffer.cpp; sourceTree = "<group>"; };
|
FADF53F61E3C7ACD00012CC0 /* Buffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Buffer.cpp; sourceTree = "<group>"; };
|
||||||
FADF53F71E3C7ACD00012CC0 /* Buffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Buffer.h; sourceTree = "<group>"; };
|
FADF53F71E3C7ACD00012CC0 /* Buffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Buffer.h; sourceTree = "<group>"; };
|
||||||
FADF53FB1E3D74F200012CC0 /* Text.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Text.cpp; sourceTree = "<group>"; };
|
FADF53FB1E3D74F200012CC0 /* Text.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Text.cpp; sourceTree = "<group>"; };
|
||||||
@@ -2738,8 +2733,6 @@
|
|||||||
FA28EBD41E352DB5003446F4 /* BufferSync.h */,
|
FA28EBD41E352DB5003446F4 /* BufferSync.h */,
|
||||||
FA0B7B8D1A95902C000E1D17 /* Canvas.cpp */,
|
FA0B7B8D1A95902C000E1D17 /* Canvas.cpp */,
|
||||||
FA0B7B8E1A95902C000E1D17 /* Canvas.h */,
|
FA0B7B8E1A95902C000E1D17 /* Canvas.h */,
|
||||||
FA0B7B8F1A95902C000E1D17 /* Font.cpp */,
|
|
||||||
FA0B7B901A95902C000E1D17 /* Font.h */,
|
|
||||||
FA0B7B911A95902C000E1D17 /* Graphics.cpp */,
|
FA0B7B911A95902C000E1D17 /* Graphics.cpp */,
|
||||||
FA0B7B921A95902C000E1D17 /* Graphics.h */,
|
FA0B7B921A95902C000E1D17 /* Graphics.h */,
|
||||||
FA0B7B931A95902C000E1D17 /* Image.cpp */,
|
FA0B7B931A95902C000E1D17 /* Image.cpp */,
|
||||||
@@ -2758,8 +2751,6 @@
|
|||||||
FA7634491E28722A0066EF9E /* StreamBuffer.h */,
|
FA7634491E28722A0066EF9E /* StreamBuffer.h */,
|
||||||
FA0B7BA11A95902C000E1D17 /* Text.cpp */,
|
FA0B7BA11A95902C000E1D17 /* Text.cpp */,
|
||||||
FA0B7BA21A95902C000E1D17 /* Text.h */,
|
FA0B7BA21A95902C000E1D17 /* Text.h */,
|
||||||
FA27B3C31B4985D8008A9DCE /* Video.cpp */,
|
|
||||||
FA27B3C41B4985D8008A9DCE /* Video.h */,
|
|
||||||
);
|
);
|
||||||
path = opengl;
|
path = opengl;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -2773,6 +2764,8 @@
|
|||||||
FA0B7BC51A95902C000E1D17 /* Image.h */,
|
FA0B7BC51A95902C000E1D17 /* Image.h */,
|
||||||
FA0B7BC61A95902C000E1D17 /* ImageData.cpp */,
|
FA0B7BC61A95902C000E1D17 /* ImageData.cpp */,
|
||||||
FA0B7BC71A95902C000E1D17 /* ImageData.h */,
|
FA0B7BC71A95902C000E1D17 /* ImageData.h */,
|
||||||
|
FAD19A151DFF8CA200D5398A /* ImageDataBase.cpp */,
|
||||||
|
FAD19A161DFF8CA200D5398A /* ImageDataBase.h */,
|
||||||
FA0B7BC81A95902C000E1D17 /* magpie */,
|
FA0B7BC81A95902C000E1D17 /* magpie */,
|
||||||
FA0B7BE21A95902C000E1D17 /* wrap_CompressedImageData.cpp */,
|
FA0B7BE21A95902C000E1D17 /* wrap_CompressedImageData.cpp */,
|
||||||
FA0B7BE31A95902C000E1D17 /* wrap_CompressedImageData.h */,
|
FA0B7BE31A95902C000E1D17 /* wrap_CompressedImageData.h */,
|
||||||
@@ -3533,7 +3526,6 @@
|
|||||||
FAF1405C1E20934C00F898D2 /* InitializeGlobals.h in Headers */,
|
FAF1405C1E20934C00F898D2 /* InitializeGlobals.h in Headers */,
|
||||||
FA0B7DB31A95902C000E1D17 /* wrap_Image.h in Headers */,
|
FA0B7DB31A95902C000E1D17 /* wrap_Image.h in Headers */,
|
||||||
FA0B7D531A95902C000E1D17 /* Text.h in Headers */,
|
FA0B7D531A95902C000E1D17 /* Text.h in Headers */,
|
||||||
FA0B7D381A95902C000E1D17 /* Font.h in Headers */,
|
|
||||||
FAF140591E20934C00F898D2 /* Common.h in Headers */,
|
FAF140591E20934C00F898D2 /* Common.h in Headers */,
|
||||||
FA0B7EC31A95902C000E1D17 /* threads.h in Headers */,
|
FA0B7EC31A95902C000E1D17 /* threads.h in Headers */,
|
||||||
FA0B7AC21A958EA3000E1D17 /* enet.h in Headers */,
|
FA0B7AC21A958EA3000E1D17 /* enet.h in Headers */,
|
||||||
@@ -3594,7 +3586,6 @@
|
|||||||
FA0B7E681A95902C000E1D17 /* wrap_PrismaticJoint.h in Headers */,
|
FA0B7E681A95902C000E1D17 /* wrap_PrismaticJoint.h in Headers */,
|
||||||
FA0B7D571A95902C000E1D17 /* Buffer.h in Headers */,
|
FA0B7D571A95902C000E1D17 /* Buffer.h in Headers */,
|
||||||
FAF140AB1E20934C00F898D2 /* SymbolTable.h in Headers */,
|
FAF140AB1E20934C00F898D2 /* SymbolTable.h in Headers */,
|
||||||
FA27B3C71B4985D8008A9DCE /* Video.h in Headers */,
|
|
||||||
FA0B7ED71A95902D000E1D17 /* Timer.h in Headers */,
|
FA0B7ED71A95902D000E1D17 /* Timer.h in Headers */,
|
||||||
FA0B7AC31A958EA3000E1D17 /* list.h in Headers */,
|
FA0B7AC31A958EA3000E1D17 /* list.h in Headers */,
|
||||||
FA0B7B2D1A958EA3000E1D17 /* core.h in Headers */,
|
FA0B7B2D1A958EA3000E1D17 /* core.h in Headers */,
|
||||||
@@ -3790,6 +3781,7 @@
|
|||||||
FA0B7D841A95902C000E1D17 /* CompressedImageData.h in Headers */,
|
FA0B7D841A95902C000E1D17 /* CompressedImageData.h in Headers */,
|
||||||
FAF1407E1E20934C00F898D2 /* LiveTraverser.h in Headers */,
|
FAF1407E1E20934C00F898D2 /* LiveTraverser.h in Headers */,
|
||||||
FA0B7D231A95902C000E1D17 /* Rasterizer.h in Headers */,
|
FA0B7D231A95902C000E1D17 /* Rasterizer.h in Headers */,
|
||||||
|
FAD19A191DFF8CA200D5398A /* ImageDataBase.h in Headers */,
|
||||||
FA0B7CDB1A95902C000E1D17 /* Pool.h in Headers */,
|
FA0B7CDB1A95902C000E1D17 /* Pool.h in Headers */,
|
||||||
FA0B7D0B1A95902C000E1D17 /* wrap_FileData.h in Headers */,
|
FA0B7D0B1A95902C000E1D17 /* wrap_FileData.h in Headers */,
|
||||||
FA0B7DF91A95902C000E1D17 /* Body.h in Headers */,
|
FA0B7DF91A95902C000E1D17 /* Body.h in Headers */,
|
||||||
@@ -4048,7 +4040,6 @@
|
|||||||
FA0B7DB81A95902C000E1D17 /* Joystick.cpp in Sources */,
|
FA0B7DB81A95902C000E1D17 /* Joystick.cpp in Sources */,
|
||||||
FA0B794B1A958E3B000E1D17 /* wrap_Data.cpp in Sources */,
|
FA0B794B1A958E3B000E1D17 /* wrap_Data.cpp in Sources */,
|
||||||
FAB17BEC1ABFAF1800F9BA27 /* CompressedData.cpp in Sources */,
|
FAB17BEC1ABFAF1800F9BA27 /* CompressedData.cpp in Sources */,
|
||||||
FA0B7D371A95902C000E1D17 /* Font.cpp in Sources */,
|
|
||||||
FA0B7E401A95902C000E1D17 /* wrap_ChainShape.cpp in Sources */,
|
FA0B7E401A95902C000E1D17 /* wrap_ChainShape.cpp in Sources */,
|
||||||
FA0B7DEC1A95902C000E1D17 /* Cursor.cpp in Sources */,
|
FA0B7DEC1A95902C000E1D17 /* Cursor.cpp in Sources */,
|
||||||
FA0B7D871A95902C000E1D17 /* ImageData.cpp in Sources */,
|
FA0B7D871A95902C000E1D17 /* ImageData.cpp in Sources */,
|
||||||
@@ -4126,7 +4117,6 @@
|
|||||||
FA0B7A7B1A958EA3000E1D17 /* b2Contact.cpp in Sources */,
|
FA0B7A7B1A958EA3000E1D17 /* b2Contact.cpp in Sources */,
|
||||||
FA0B7A6F1A958EA3000E1D17 /* b2WorldCallbacks.cpp in Sources */,
|
FA0B7A6F1A958EA3000E1D17 /* b2WorldCallbacks.cpp in Sources */,
|
||||||
FA0B793C1A958E3B000E1D17 /* runtime.cpp in Sources */,
|
FA0B793C1A958E3B000E1D17 /* runtime.cpp in Sources */,
|
||||||
FA27B3C61B4985D8008A9DCE /* Video.cpp in Sources */,
|
|
||||||
FAF140DC1E20934C00F898D2 /* InitializeDll.cpp in Sources */,
|
FAF140DC1E20934C00F898D2 /* InitializeDll.cpp in Sources */,
|
||||||
FA0B7DBC1A95902C000E1D17 /* Joystick.cpp in Sources */,
|
FA0B7DBC1A95902C000E1D17 /* Joystick.cpp in Sources */,
|
||||||
FA0B7DAF1A95902C000E1D17 /* wrap_CompressedImageData.cpp in Sources */,
|
FA0B7DAF1A95902C000E1D17 /* wrap_CompressedImageData.cpp in Sources */,
|
||||||
@@ -4194,6 +4184,7 @@
|
|||||||
FA0B7A2A1A958EA3000E1D17 /* b2BroadPhase.cpp in Sources */,
|
FA0B7A2A1A958EA3000E1D17 /* b2BroadPhase.cpp in Sources */,
|
||||||
FA0B7A811A958EA3000E1D17 /* b2EdgeAndCircleContact.cpp in Sources */,
|
FA0B7A811A958EA3000E1D17 /* b2EdgeAndCircleContact.cpp in Sources */,
|
||||||
FA0B7D071A95902C000E1D17 /* wrap_File.cpp in Sources */,
|
FA0B7D071A95902C000E1D17 /* wrap_File.cpp in Sources */,
|
||||||
|
FAD19A181DFF8CA200D5398A /* ImageDataBase.cpp in Sources */,
|
||||||
FA0B7AD01A958EA3000E1D17 /* peer.c in Sources */,
|
FA0B7AD01A958EA3000E1D17 /* peer.c in Sources */,
|
||||||
FA27B3C11B4985BF008A9DCE /* wrap_VideoStream.cpp in Sources */,
|
FA27B3C11B4985BF008A9DCE /* wrap_VideoStream.cpp in Sources */,
|
||||||
FADF54211E3DA52C00012CC0 /* wrap_ParticleSystem.cpp in Sources */,
|
FADF54211E3DA52C00012CC0 /* wrap_ParticleSystem.cpp in Sources */,
|
||||||
@@ -4409,7 +4400,6 @@
|
|||||||
FA0B7DB71A95902C000E1D17 /* Joystick.cpp in Sources */,
|
FA0B7DB71A95902C000E1D17 /* Joystick.cpp in Sources */,
|
||||||
FA0B7A321A958EA3000E1D17 /* b2Collision.cpp in Sources */,
|
FA0B7A321A958EA3000E1D17 /* b2Collision.cpp in Sources */,
|
||||||
FAB17BEB1ABFAF1800F9BA27 /* CompressedData.cpp in Sources */,
|
FAB17BEB1ABFAF1800F9BA27 /* CompressedData.cpp in Sources */,
|
||||||
FA0B7D361A95902C000E1D17 /* Font.cpp in Sources */,
|
|
||||||
FA0B7E3F1A95902C000E1D17 /* wrap_ChainShape.cpp in Sources */,
|
FA0B7E3F1A95902C000E1D17 /* wrap_ChainShape.cpp in Sources */,
|
||||||
FA0B7DEB1A95902C000E1D17 /* Cursor.cpp in Sources */,
|
FA0B7DEB1A95902C000E1D17 /* Cursor.cpp in Sources */,
|
||||||
FA0B7D861A95902C000E1D17 /* ImageData.cpp in Sources */,
|
FA0B7D861A95902C000E1D17 /* ImageData.cpp in Sources */,
|
||||||
@@ -4486,7 +4476,6 @@
|
|||||||
FAF140AC1E20934C00F898D2 /* Versions.cpp in Sources */,
|
FAF140AC1E20934C00F898D2 /* Versions.cpp in Sources */,
|
||||||
FA0B7A441A958EA3000E1D17 /* b2EdgeShape.cpp in Sources */,
|
FA0B7A441A958EA3000E1D17 /* b2EdgeShape.cpp in Sources */,
|
||||||
FA0B79461A958E3B000E1D17 /* Vector.cpp in Sources */,
|
FA0B79461A958E3B000E1D17 /* Vector.cpp in Sources */,
|
||||||
FA27B3C51B4985D8008A9DCE /* Video.cpp in Sources */,
|
|
||||||
FA0B7DBB1A95902C000E1D17 /* Joystick.cpp in Sources */,
|
FA0B7DBB1A95902C000E1D17 /* Joystick.cpp in Sources */,
|
||||||
FAF140DB1E20934C00F898D2 /* InitializeDll.cpp in Sources */,
|
FAF140DB1E20934C00F898D2 /* InitializeDll.cpp in Sources */,
|
||||||
FA0B7DAE1A95902C000E1D17 /* wrap_CompressedImageData.cpp in Sources */,
|
FA0B7DAE1A95902C000E1D17 /* wrap_CompressedImageData.cpp in Sources */,
|
||||||
@@ -4555,6 +4544,7 @@
|
|||||||
FA0B7A951A958EA3000E1D17 /* b2Joint.cpp in Sources */,
|
FA0B7A951A958EA3000E1D17 /* b2Joint.cpp in Sources */,
|
||||||
FA0B7D061A95902C000E1D17 /* wrap_File.cpp in Sources */,
|
FA0B7D061A95902C000E1D17 /* wrap_File.cpp in Sources */,
|
||||||
FA0B7A4E1A958EA3000E1D17 /* b2Draw.cpp in Sources */,
|
FA0B7A4E1A958EA3000E1D17 /* b2Draw.cpp in Sources */,
|
||||||
|
FAD19A171DFF8CA200D5398A /* ImageDataBase.cpp in Sources */,
|
||||||
FA27B3C01B4985BF008A9DCE /* wrap_VideoStream.cpp in Sources */,
|
FA27B3C01B4985BF008A9DCE /* wrap_VideoStream.cpp in Sources */,
|
||||||
FA0B7D931A95902C000E1D17 /* Image.cpp in Sources */,
|
FA0B7D931A95902C000E1D17 /* Image.cpp in Sources */,
|
||||||
FADF54201E3DA52C00012CC0 /* wrap_ParticleSystem.cpp in Sources */,
|
FADF54201E3DA52C00012CC0 /* wrap_ParticleSystem.cpp in Sources */,
|
||||||
|
|||||||
@@ -130,6 +130,17 @@ void luax_printstack(lua_State *L)
|
|||||||
std::cout << i << " - " << luaL_typename(L, i) << std::endl;
|
std::cout << i << " - " << luaL_typename(L, i) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool luax_isarrayoftables(lua_State *L, int idx)
|
||||||
|
{
|
||||||
|
if (!lua_istable(L, idx))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
lua_rawgeti(L, idx, 1);
|
||||||
|
bool tableoftables = lua_istable(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return tableoftables;
|
||||||
|
}
|
||||||
|
|
||||||
bool luax_toboolean(lua_State *L, int idx)
|
bool luax_toboolean(lua_State *L, int idx)
|
||||||
{
|
{
|
||||||
return (lua_toboolean(L, idx) != 0);
|
return (lua_toboolean(L, idx) != 0);
|
||||||
@@ -208,6 +219,23 @@ double luax_numberflag(lua_State *L, int table_index, const char *key, double de
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int luax_checkintflag(lua_State *L, int table_index, const char *key)
|
||||||
|
{
|
||||||
|
lua_getfield(L, table_index, key);
|
||||||
|
|
||||||
|
int retval;
|
||||||
|
if (!lua_isnumber(L, -1))
|
||||||
|
{
|
||||||
|
std::string err = "expected integer field " + std::string(key) + " in table";
|
||||||
|
return luaL_argerror(L, table_index, err.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
retval = (int) luaL_checkinteger(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
int luax_assert_argc(lua_State *L, int min)
|
int luax_assert_argc(lua_State *L, int min)
|
||||||
{
|
{
|
||||||
int argc = lua_gettop(L);
|
int argc = lua_gettop(L);
|
||||||
|
|||||||
@@ -106,6 +106,11 @@ Reference *luax_refif(lua_State *L, int type);
|
|||||||
**/
|
**/
|
||||||
void luax_printstack(lua_State *L);
|
void luax_printstack(lua_State *L);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether the value at idx is an array of tables.
|
||||||
|
**/
|
||||||
|
bool luax_isarrayoftables(lua_State *L, int idx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the value at idx to a bool. It follow the same rules
|
* Converts the value at idx to a bool. It follow the same rules
|
||||||
* as lua_toboolean, but returns a bool instead of an int.
|
* as lua_toboolean, but returns a bool instead of an int.
|
||||||
@@ -163,6 +168,8 @@ bool luax_boolflag(lua_State *L, int table_index, const char *key, bool defaultV
|
|||||||
int luax_intflag(lua_State *L, int table_index, const char *key, int defaultValue);
|
int luax_intflag(lua_State *L, int table_index, const char *key, int defaultValue);
|
||||||
double luax_numberflag(lua_State *L, int table_index, const char *key, double defaultValue);
|
double luax_numberflag(lua_State *L, int table_index, const char *key, double defaultValue);
|
||||||
|
|
||||||
|
int luax_checkintflag(lua_State *L, int table_index, const char *key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the value at the specified index to an Lua number, and then
|
* Convert the value at the specified index to an Lua number, and then
|
||||||
* convert to a float.
|
* convert to a float.
|
||||||
|
|||||||
@@ -207,6 +207,8 @@ enum TBuiltInVariable {
|
|||||||
EbvViewportMaskNV,
|
EbvViewportMaskNV,
|
||||||
EbvSecondaryPositionNV,
|
EbvSecondaryPositionNV,
|
||||||
EbvSecondaryViewportMaskNV,
|
EbvSecondaryViewportMaskNV,
|
||||||
|
EbvPositionPerViewNV,
|
||||||
|
EbvViewportMaskPerViewNV,
|
||||||
#endif
|
#endif
|
||||||
// HLSL built-ins that live only temporarily, until they get remapped
|
// HLSL built-ins that live only temporarily, until they get remapped
|
||||||
// to one of the above.
|
// to one of the above.
|
||||||
@@ -325,6 +327,8 @@ __inline const char* GetBuiltInVariableString(TBuiltInVariable v)
|
|||||||
case EbvViewportMaskNV: return "ViewportMaskNV";
|
case EbvViewportMaskNV: return "ViewportMaskNV";
|
||||||
case EbvSecondaryPositionNV: return "SecondaryPositionNV";
|
case EbvSecondaryPositionNV: return "SecondaryPositionNV";
|
||||||
case EbvSecondaryViewportMaskNV: return "SecondaryViewportMaskNV";
|
case EbvSecondaryViewportMaskNV: return "SecondaryViewportMaskNV";
|
||||||
|
case EbvPositionPerViewNV: return "PositionPerViewNV";
|
||||||
|
case EbvViewportMaskPerViewNV: return "ViewportMaskPerViewNV";
|
||||||
#endif
|
#endif
|
||||||
default: return "unknown built-in variable";
|
default: return "unknown built-in variable";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,12 +81,19 @@ public:
|
|||||||
type = EbtBool;
|
type = EbtBool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setSConst(const TString* s)
|
||||||
|
{
|
||||||
|
sConst = s;
|
||||||
|
type = EbtString;
|
||||||
|
}
|
||||||
|
|
||||||
int getIConst() const { return iConst; }
|
int getIConst() const { return iConst; }
|
||||||
unsigned int getUConst() const { return uConst; }
|
unsigned int getUConst() const { return uConst; }
|
||||||
long long getI64Const() const { return i64Const; }
|
long long getI64Const() const { return i64Const; }
|
||||||
unsigned long long getU64Const() const { return u64Const; }
|
unsigned long long getU64Const() const { return u64Const; }
|
||||||
double getDConst() const { return dConst; }
|
double getDConst() const { return dConst; }
|
||||||
bool getBConst() const { return bConst; }
|
bool getBConst() const { return bConst; }
|
||||||
|
const TString* getSConst() const { return sConst; }
|
||||||
|
|
||||||
bool operator==(const int i) const
|
bool operator==(const int i) const
|
||||||
{
|
{
|
||||||
@@ -532,6 +539,7 @@ private:
|
|||||||
unsigned long long u64Const; // used for u64vec, scalar uint64s
|
unsigned long long u64Const; // used for u64vec, scalar uint64s
|
||||||
bool bConst; // used for bvec, scalar bools
|
bool bConst; // used for bvec, scalar bools
|
||||||
double dConst; // used for vec, dvec, mat, dmat, scalar floats and doubles
|
double dConst; // used for vec, dvec, mat, dmat, scalar floats and doubles
|
||||||
|
const TString* sConst; // string constant
|
||||||
};
|
};
|
||||||
|
|
||||||
TBasicType type;
|
TBasicType type;
|
||||||
|
|||||||
@@ -403,6 +403,21 @@ public:
|
|||||||
{
|
{
|
||||||
storage = EvqTemporary;
|
storage = EvqTemporary;
|
||||||
builtIn = EbvNone;
|
builtIn = EbvNone;
|
||||||
|
clearInterstage();
|
||||||
|
clearMemory();
|
||||||
|
specConstant = false;
|
||||||
|
clearLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearInterstage()
|
||||||
|
{
|
||||||
|
clearInterpolation();
|
||||||
|
patch = false;
|
||||||
|
sample = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearInterpolation()
|
||||||
|
{
|
||||||
centroid = false;
|
centroid = false;
|
||||||
smooth = false;
|
smooth = false;
|
||||||
flat = false;
|
flat = false;
|
||||||
@@ -410,15 +425,15 @@ public:
|
|||||||
#ifdef AMD_EXTENSIONS
|
#ifdef AMD_EXTENSIONS
|
||||||
explicitInterp = false;
|
explicitInterp = false;
|
||||||
#endif
|
#endif
|
||||||
patch = false;
|
}
|
||||||
sample = false;
|
|
||||||
|
void clearMemory()
|
||||||
|
{
|
||||||
coherent = false;
|
coherent = false;
|
||||||
volatil = false;
|
volatil = false;
|
||||||
restrict = false;
|
restrict = false;
|
||||||
readonly = false;
|
readonly = false;
|
||||||
writeonly = false;
|
writeonly = false;
|
||||||
specConstant = false;
|
|
||||||
clearLayout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop just the storage qualification, which perhaps should
|
// Drop just the storage qualification, which perhaps should
|
||||||
@@ -575,42 +590,47 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Implementing an embedded layout-qualifier class here, since C++ can't have a real class bitfield
|
// Implementing an embedded layout-qualifier class here, since C++ can't have a real class bitfield
|
||||||
void clearLayout()
|
void clearLayout() // all layout
|
||||||
{
|
{
|
||||||
layoutMatrix = ElmNone;
|
clearUniformLayout();
|
||||||
layoutPacking = ElpNone;
|
|
||||||
layoutOffset = layoutNotSet;
|
|
||||||
layoutAlign = layoutNotSet;
|
|
||||||
|
|
||||||
layoutLocation = layoutLocationEnd;
|
|
||||||
layoutComponent = layoutComponentEnd;
|
|
||||||
layoutSet = layoutSetEnd;
|
|
||||||
layoutBinding = layoutBindingEnd;
|
|
||||||
layoutIndex = layoutIndexEnd;
|
|
||||||
|
|
||||||
layoutStream = layoutStreamEnd;
|
|
||||||
|
|
||||||
layoutXfbBuffer = layoutXfbBufferEnd;
|
|
||||||
layoutXfbStride = layoutXfbStrideEnd;
|
|
||||||
layoutXfbOffset = layoutXfbOffsetEnd;
|
|
||||||
layoutAttachment = layoutAttachmentEnd;
|
|
||||||
layoutSpecConstantId = layoutSpecConstantIdEnd;
|
|
||||||
|
|
||||||
layoutFormat = ElfNone;
|
|
||||||
|
|
||||||
layoutPushConstant = false;
|
layoutPushConstant = false;
|
||||||
#ifdef NV_EXTENSIONS
|
#ifdef NV_EXTENSIONS
|
||||||
layoutPassthrough = false;
|
layoutPassthrough = false;
|
||||||
layoutViewportRelative = false;
|
layoutViewportRelative = false;
|
||||||
// -2048 as the default vaule indicating layoutSecondaryViewportRelative is not set
|
// -2048 as the default value indicating layoutSecondaryViewportRelative is not set
|
||||||
layoutSecondaryViewportRelativeOffset = -2048;
|
layoutSecondaryViewportRelativeOffset = -2048;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
clearInterstageLayout();
|
||||||
|
|
||||||
|
layoutSpecConstantId = layoutSpecConstantIdEnd;
|
||||||
|
|
||||||
|
layoutFormat = ElfNone;
|
||||||
}
|
}
|
||||||
|
void clearInterstageLayout()
|
||||||
|
{
|
||||||
|
layoutLocation = layoutLocationEnd;
|
||||||
|
layoutComponent = layoutComponentEnd;
|
||||||
|
layoutIndex = layoutIndexEnd;
|
||||||
|
clearStreamLayout();
|
||||||
|
clearXfbLayout();
|
||||||
|
}
|
||||||
|
void clearStreamLayout()
|
||||||
|
{
|
||||||
|
layoutStream = layoutStreamEnd;
|
||||||
|
}
|
||||||
|
void clearXfbLayout()
|
||||||
|
{
|
||||||
|
layoutXfbBuffer = layoutXfbBufferEnd;
|
||||||
|
layoutXfbStride = layoutXfbStrideEnd;
|
||||||
|
layoutXfbOffset = layoutXfbOffsetEnd;
|
||||||
|
}
|
||||||
|
|
||||||
bool hasLayout() const
|
bool hasLayout() const
|
||||||
{
|
{
|
||||||
return hasUniformLayout() ||
|
return hasUniformLayout() ||
|
||||||
hasAnyLocation() ||
|
hasAnyLocation() ||
|
||||||
hasBinding() ||
|
|
||||||
hasStream() ||
|
hasStream() ||
|
||||||
hasXfb() ||
|
hasXfb() ||
|
||||||
hasFormat() ||
|
hasFormat() ||
|
||||||
@@ -670,8 +690,21 @@ public:
|
|||||||
hasPacking() ||
|
hasPacking() ||
|
||||||
hasOffset() ||
|
hasOffset() ||
|
||||||
hasBinding() ||
|
hasBinding() ||
|
||||||
|
hasSet() ||
|
||||||
hasAlign();
|
hasAlign();
|
||||||
}
|
}
|
||||||
|
void clearUniformLayout() // only uniform specific
|
||||||
|
{
|
||||||
|
layoutMatrix = ElmNone;
|
||||||
|
layoutPacking = ElpNone;
|
||||||
|
layoutOffset = layoutNotSet;
|
||||||
|
layoutAlign = layoutNotSet;
|
||||||
|
|
||||||
|
layoutSet = layoutSetEnd;
|
||||||
|
layoutBinding = layoutBindingEnd;
|
||||||
|
layoutAttachment = layoutAttachmentEnd;
|
||||||
|
}
|
||||||
|
|
||||||
bool hasMatrix() const
|
bool hasMatrix() const
|
||||||
{
|
{
|
||||||
return layoutMatrix != ElmNone;
|
return layoutMatrix != ElmNone;
|
||||||
@@ -1202,30 +1235,11 @@ public:
|
|||||||
typeName = copyOf.typeName;
|
typeName = copyOf.typeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make complete copy of the whole type graph rooted at 'copyOf'.
|
||||||
void deepCopy(const TType& copyOf)
|
void deepCopy(const TType& copyOf)
|
||||||
{
|
{
|
||||||
shallowCopy(copyOf);
|
TMap<TTypeList*,TTypeList*> copied; // to enable copying a type graph as a graph, not a tree
|
||||||
|
deepCopy(copyOf, copied);
|
||||||
if (copyOf.arraySizes) {
|
|
||||||
arraySizes = new TArraySizes;
|
|
||||||
*arraySizes = *copyOf.arraySizes;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (copyOf.structure) {
|
|
||||||
structure = new TTypeList;
|
|
||||||
for (unsigned int i = 0; i < copyOf.structure->size(); ++i) {
|
|
||||||
TTypeLoc typeLoc;
|
|
||||||
typeLoc.loc = (*copyOf.structure)[i].loc;
|
|
||||||
typeLoc.type = new TType();
|
|
||||||
typeLoc.type->deepCopy(*(*copyOf.structure)[i].type);
|
|
||||||
structure->push_back(typeLoc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (copyOf.fieldName)
|
|
||||||
fieldName = NewPoolTString(copyOf.fieldName->c_str());
|
|
||||||
if (copyOf.typeName)
|
|
||||||
typeName = NewPoolTString(copyOf.typeName->c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively make temporary
|
// Recursively make temporary
|
||||||
@@ -1346,6 +1360,8 @@ public:
|
|||||||
case EbvViewportMaskNV:
|
case EbvViewportMaskNV:
|
||||||
case EbvSecondaryPositionNV:
|
case EbvSecondaryPositionNV:
|
||||||
case EbvSecondaryViewportMaskNV:
|
case EbvSecondaryViewportMaskNV:
|
||||||
|
case EbvPositionPerViewNV:
|
||||||
|
case EbvViewportMaskPerViewNV:
|
||||||
#endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
@@ -1720,6 +1736,7 @@ public:
|
|||||||
const char* getBuiltInVariableString() const { return GetBuiltInVariableString(qualifier.builtIn); }
|
const char* getBuiltInVariableString() const { return GetBuiltInVariableString(qualifier.builtIn); }
|
||||||
const char* getPrecisionQualifierString() const { return GetPrecisionQualifierString(qualifier.precision); }
|
const char* getPrecisionQualifierString() const { return GetPrecisionQualifierString(qualifier.precision); }
|
||||||
const TTypeList* getStruct() const { return structure; }
|
const TTypeList* getStruct() const { return structure; }
|
||||||
|
void setStruct(TTypeList* s) { structure = s; }
|
||||||
TTypeList* getWritableStruct() const { return structure; } // This should only be used when known to not be sharing with other threads
|
TTypeList* getWritableStruct() const { return structure; } // This should only be used when known to not be sharing with other threads
|
||||||
|
|
||||||
int computeNumComponents() const
|
int computeNumComponents() const
|
||||||
@@ -1830,6 +1847,42 @@ protected:
|
|||||||
TType(const TType& type);
|
TType(const TType& type);
|
||||||
TType& operator=(const TType& type);
|
TType& operator=(const TType& type);
|
||||||
|
|
||||||
|
// Recursively copy a type graph, while preserving the graph-like
|
||||||
|
// quality. That is, don't make more than one copy of a structure that
|
||||||
|
// gets reused multiple times in the type graph.
|
||||||
|
void deepCopy(const TType& copyOf, TMap<TTypeList*,TTypeList*>& copiedMap)
|
||||||
|
{
|
||||||
|
shallowCopy(copyOf);
|
||||||
|
|
||||||
|
if (copyOf.arraySizes) {
|
||||||
|
arraySizes = new TArraySizes;
|
||||||
|
*arraySizes = *copyOf.arraySizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copyOf.structure) {
|
||||||
|
auto prevCopy = copiedMap.find(copyOf.structure);
|
||||||
|
if (prevCopy != copiedMap.end())
|
||||||
|
structure = prevCopy->second;
|
||||||
|
else {
|
||||||
|
structure = new TTypeList;
|
||||||
|
copiedMap[copyOf.structure] = structure;
|
||||||
|
for (unsigned int i = 0; i < copyOf.structure->size(); ++i) {
|
||||||
|
TTypeLoc typeLoc;
|
||||||
|
typeLoc.loc = (*copyOf.structure)[i].loc;
|
||||||
|
typeLoc.type = new TType();
|
||||||
|
typeLoc.type->deepCopy(*(*copyOf.structure)[i].type, copiedMap);
|
||||||
|
structure->push_back(typeLoc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copyOf.fieldName)
|
||||||
|
fieldName = NewPoolTString(copyOf.fieldName->c_str());
|
||||||
|
if (copyOf.typeName)
|
||||||
|
typeName = NewPoolTString(copyOf.typeName->c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void buildMangledName(TString&);
|
void buildMangledName(TString&);
|
||||||
|
|
||||||
TBasicType basicType : 8;
|
TBasicType basicType : 8;
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
// For the version, it uses the latest git tag followed by the number of commits.
|
// For the version, it uses the latest git tag followed by the number of commits.
|
||||||
// For the date, it uses the current date (when then script is run).
|
// For the date, it uses the current date (when then script is run).
|
||||||
|
|
||||||
#define GLSLANG_REVISION "Overload400-PrecQual.1773"
|
#define GLSLANG_REVISION "Overload400-PrecQual.1843"
|
||||||
#define GLSLANG_DATE "19-Jan-2017"
|
#define GLSLANG_DATE "18-Feb-2017"
|
||||||
|
|||||||
@@ -1292,6 +1292,8 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
|
|||||||
|
|
||||||
"vec4 textureCube(samplerCube, vec3);"
|
"vec4 textureCube(samplerCube, vec3);"
|
||||||
|
|
||||||
|
"vec4 texture2DArray(sampler2DArray, vec3);" // GL_EXT_texture_array
|
||||||
|
|
||||||
"\n");
|
"\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1317,6 +1319,10 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
|
|||||||
"vec4 shadow2DRect(sampler2DRectShadow, vec3);" // GL_ARB_texture_rectangle, caught by keyword check
|
"vec4 shadow2DRect(sampler2DRectShadow, vec3);" // GL_ARB_texture_rectangle, caught by keyword check
|
||||||
"vec4 shadow2DRectProj(sampler2DRectShadow, vec4);" // GL_ARB_texture_rectangle, caught by keyword check
|
"vec4 shadow2DRectProj(sampler2DRectShadow, vec4);" // GL_ARB_texture_rectangle, caught by keyword check
|
||||||
|
|
||||||
|
"vec4 texture1DArray(sampler1DArray, vec2);" // GL_EXT_texture_array
|
||||||
|
"vec4 shadow1DArray(sampler1DArrayShadow, vec3);" // GL_EXT_texture_array
|
||||||
|
"vec4 shadow2DArray(sampler2DArrayShadow, vec4);" // GL_EXT_texture_array
|
||||||
|
|
||||||
"\n");
|
"\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2646,6 +2652,8 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
|
|||||||
"vec4 texture3DProjLod(sampler3D, vec4, float);" // GL_ARB_shader_texture_lod // OES_texture_3D, but caught by keyword check
|
"vec4 texture3DProjLod(sampler3D, vec4, float);" // GL_ARB_shader_texture_lod // OES_texture_3D, but caught by keyword check
|
||||||
"vec4 textureCubeLod(samplerCube, vec3, float);" // GL_ARB_shader_texture_lod
|
"vec4 textureCubeLod(samplerCube, vec3, float);" // GL_ARB_shader_texture_lod
|
||||||
|
|
||||||
|
"vec4 texture2DArrayLod(sampler2DArray, vec3, float);" // GL_EXT_texture_array
|
||||||
|
|
||||||
"\n");
|
"\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2681,6 +2689,10 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
|
|||||||
"vec4 shadow2DRectGradARB( sampler2DRectShadow, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod
|
"vec4 shadow2DRectGradARB( sampler2DRectShadow, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod
|
||||||
"vec4 shadow2DRectProjGradARB(sampler2DRectShadow, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod
|
"vec4 shadow2DRectProjGradARB(sampler2DRectShadow, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod
|
||||||
|
|
||||||
|
"vec4 texture1DArrayLod(sampler1DArray, vec2, float);" // GL_EXT_texture_array
|
||||||
|
"vec4 shadow1DArrayLod(sampler1DArrayShadow, vec3, float);" // GL_EXT_texture_array
|
||||||
|
"vec4 shadow2DArrayLod(sampler2DArrayShadow, vec4, float);" // GL_EXT_texture_array
|
||||||
|
|
||||||
"\n");
|
"\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2752,6 +2764,7 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
|
|||||||
"vec4 texture3D(sampler3D, vec3, float);" // OES_texture_3D
|
"vec4 texture3D(sampler3D, vec3, float);" // OES_texture_3D
|
||||||
"vec4 texture3DProj(sampler3D, vec4, float);" // OES_texture_3D
|
"vec4 texture3DProj(sampler3D, vec4, float);" // OES_texture_3D
|
||||||
"vec4 textureCube(samplerCube, vec3, float);"
|
"vec4 textureCube(samplerCube, vec3, float);"
|
||||||
|
"vec4 texture2DArray(sampler2DArray, vec3, float);" // GL_EXT_texture_array
|
||||||
|
|
||||||
"\n");
|
"\n");
|
||||||
}
|
}
|
||||||
@@ -2764,7 +2777,9 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
|
|||||||
"vec4 shadow2D(sampler2DShadow, vec3, float);"
|
"vec4 shadow2D(sampler2DShadow, vec3, float);"
|
||||||
"vec4 shadow1DProj(sampler1DShadow, vec4, float);"
|
"vec4 shadow1DProj(sampler1DShadow, vec4, float);"
|
||||||
"vec4 shadow2DProj(sampler2DShadow, vec4, float);"
|
"vec4 shadow2DProj(sampler2DShadow, vec4, float);"
|
||||||
|
"vec4 texture1DArray(sampler1DArray, vec2, float);" // GL_EXT_texture_array
|
||||||
|
"vec4 shadow1DArray(sampler1DArrayShadow, vec3, float);" // GL_EXT_texture_array
|
||||||
|
"vec4 shadow2DArray(sampler2DArrayShadow, vec4, float);" // GL_EXT_texture_array
|
||||||
"\n");
|
"\n");
|
||||||
}
|
}
|
||||||
if (spvVersion.spv == 0 && profile == EEsProfile) {
|
if (spvVersion.spv == 0 && profile == EEsProfile) {
|
||||||
@@ -3249,6 +3264,8 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
|
|||||||
"out int gl_ViewportMask[];"
|
"out int gl_ViewportMask[];"
|
||||||
"out int gl_SecondaryViewportMaskNV[];"
|
"out int gl_SecondaryViewportMaskNV[];"
|
||||||
"out vec4 gl_SecondaryPositionNV;"
|
"out vec4 gl_SecondaryPositionNV;"
|
||||||
|
"out vec4 gl_PositionPerViewNV[];"
|
||||||
|
"out int gl_ViewportMaskPerViewNV[];"
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -3313,6 +3330,7 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
|
|||||||
"float gl_CullDistance[];"
|
"float gl_CullDistance[];"
|
||||||
#ifdef NV_EXTENSIONS
|
#ifdef NV_EXTENSIONS
|
||||||
"vec4 gl_SecondaryPositionNV;"
|
"vec4 gl_SecondaryPositionNV;"
|
||||||
|
"vec4 gl_PositionPerViewNV[];"
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
stageBuiltins[EShLangGeometry].append(
|
stageBuiltins[EShLangGeometry].append(
|
||||||
@@ -3365,6 +3383,8 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
|
|||||||
"out int gl_ViewportMask[];"
|
"out int gl_ViewportMask[];"
|
||||||
"out int gl_SecondaryViewportMaskNV[];"
|
"out int gl_SecondaryViewportMaskNV[];"
|
||||||
"out vec4 gl_SecondaryPositionNV;"
|
"out vec4 gl_SecondaryPositionNV;"
|
||||||
|
"out vec4 gl_PositionPerViewNV[];"
|
||||||
|
"out int gl_ViewportMaskPerViewNV[];"
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -3429,6 +3449,8 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
|
|||||||
"int gl_ViewportMask[];"
|
"int gl_ViewportMask[];"
|
||||||
"vec4 gl_SecondaryPositionNV;"
|
"vec4 gl_SecondaryPositionNV;"
|
||||||
"int gl_SecondaryViewportMaskNV[];"
|
"int gl_SecondaryViewportMaskNV[];"
|
||||||
|
"vec4 gl_PositionPerViewNV[];"
|
||||||
|
"int gl_ViewportMaskPerViewNV[];"
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
stageBuiltins[EShLangTessControl].append(
|
stageBuiltins[EShLangTessControl].append(
|
||||||
@@ -3508,6 +3530,8 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
|
|||||||
"out int gl_ViewportMask[];"
|
"out int gl_ViewportMask[];"
|
||||||
"out vec4 gl_SecondaryPositionNV;"
|
"out vec4 gl_SecondaryPositionNV;"
|
||||||
"out int gl_SecondaryViewportMaskNV[];"
|
"out int gl_SecondaryViewportMaskNV[];"
|
||||||
|
"out vec4 gl_PositionPerViewNV[];"
|
||||||
|
"out int gl_ViewportMaskPerViewNV[];"
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -4203,7 +4227,7 @@ void TBuiltIns::addSamplingFunctions(TSampler sampler, TString& typeName, int ve
|
|||||||
|
|
||||||
// Add to the per-language set of built-ins
|
// Add to the per-language set of built-ins
|
||||||
|
|
||||||
if (bias)
|
if (bias || lodClamp)
|
||||||
stageBuiltins[EShLangFragment].append(s);
|
stageBuiltins[EShLangFragment].append(s);
|
||||||
else
|
else
|
||||||
commonBuiltins.append(s);
|
commonBuiltins.append(s);
|
||||||
@@ -4446,6 +4470,7 @@ void TBuiltIns::initialize(const TBuiltInResource &resources, int version, EProf
|
|||||||
"highp float gl_PointSize;"
|
"highp float gl_PointSize;"
|
||||||
#ifdef NV_EXTENSIONS
|
#ifdef NV_EXTENSIONS
|
||||||
"highp vec4 gl_SecondaryPositionNV;"
|
"highp vec4 gl_SecondaryPositionNV;"
|
||||||
|
"highp vec4 gl_PositionPerViewNV[];"
|
||||||
#endif
|
#endif
|
||||||
"} gl_in[gl_MaxPatchVertices];"
|
"} gl_in[gl_MaxPatchVertices];"
|
||||||
"\n");
|
"\n");
|
||||||
@@ -4635,6 +4660,7 @@ void TBuiltIns::initialize(const TBuiltInResource &resources, int version, EProf
|
|||||||
"float gl_CullDistance[];"
|
"float gl_CullDistance[];"
|
||||||
#ifdef NV_EXTENSIONS
|
#ifdef NV_EXTENSIONS
|
||||||
"vec4 gl_SecondaryPositionNV;"
|
"vec4 gl_SecondaryPositionNV;"
|
||||||
|
"vec4 gl_PositionPerViewNV[];"
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
s.append(
|
s.append(
|
||||||
@@ -5033,19 +5059,26 @@ void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion
|
|||||||
symbolTable.setVariableExtensions("gl_ViewportMask", 1, &E_GL_NV_viewport_array2);
|
symbolTable.setVariableExtensions("gl_ViewportMask", 1, &E_GL_NV_viewport_array2);
|
||||||
symbolTable.setVariableExtensions("gl_SecondaryPositionNV", 1, &E_GL_NV_stereo_view_rendering);
|
symbolTable.setVariableExtensions("gl_SecondaryPositionNV", 1, &E_GL_NV_stereo_view_rendering);
|
||||||
symbolTable.setVariableExtensions("gl_SecondaryViewportMaskNV", 1, &E_GL_NV_stereo_view_rendering);
|
symbolTable.setVariableExtensions("gl_SecondaryViewportMaskNV", 1, &E_GL_NV_stereo_view_rendering);
|
||||||
|
symbolTable.setVariableExtensions("gl_PositionPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes);
|
||||||
|
symbolTable.setVariableExtensions("gl_ViewportMaskPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes);
|
||||||
|
|
||||||
BuiltInVariable("gl_ViewportMask", EbvViewportMaskNV, symbolTable);
|
BuiltInVariable("gl_ViewportMask", EbvViewportMaskNV, symbolTable);
|
||||||
BuiltInVariable("gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable);
|
BuiltInVariable("gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable);
|
||||||
BuiltInVariable("gl_SecondaryViewportMaskNV", EbvSecondaryViewportMaskNV, symbolTable);
|
BuiltInVariable("gl_SecondaryViewportMaskNV", EbvSecondaryViewportMaskNV, symbolTable);
|
||||||
|
BuiltInVariable("gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable);
|
||||||
|
BuiltInVariable("gl_ViewportMaskPerViewNV", EbvViewportMaskPerViewNV, symbolTable);
|
||||||
|
|
||||||
if (language != EShLangVertex)
|
if (language != EShLangVertex) {
|
||||||
BuiltInVariable("gl_in", "gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable);
|
BuiltInVariable("gl_in", "gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable);
|
||||||
|
BuiltInVariable("gl_in", "gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable);
|
||||||
|
}
|
||||||
BuiltInVariable("gl_out", "gl_Layer", EbvLayer, symbolTable);
|
BuiltInVariable("gl_out", "gl_Layer", EbvLayer, symbolTable);
|
||||||
BuiltInVariable("gl_out", "gl_ViewportIndex", EbvViewportIndex, symbolTable);
|
BuiltInVariable("gl_out", "gl_ViewportIndex", EbvViewportIndex, symbolTable);
|
||||||
BuiltInVariable("gl_out", "gl_ViewportMask", EbvViewportMaskNV, symbolTable);
|
BuiltInVariable("gl_out", "gl_ViewportMask", EbvViewportMaskNV, symbolTable);
|
||||||
BuiltInVariable("gl_out", "gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable);
|
BuiltInVariable("gl_out", "gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable);
|
||||||
BuiltInVariable("gl_out", "gl_SecondaryViewportMaskNV", EbvSecondaryViewportMaskNV, symbolTable);
|
BuiltInVariable("gl_out", "gl_SecondaryViewportMaskNV", EbvSecondaryViewportMaskNV, symbolTable);
|
||||||
|
BuiltInVariable("gl_out", "gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable);
|
||||||
|
BuiltInVariable("gl_out", "gl_ViewportMaskPerViewNV", EbvViewportMaskPerViewNV, symbolTable);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
BuiltInVariable("gl_PatchVerticesIn", EbvPatchVertices, symbolTable);
|
BuiltInVariable("gl_PatchVerticesIn", EbvPatchVertices, symbolTable);
|
||||||
@@ -5197,6 +5230,27 @@ void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion
|
|||||||
symbolTable.setFunctionExtensions("shadow2DRectProjGradARB", 1, &E_GL_ARB_shader_texture_lod);
|
symbolTable.setFunctionExtensions("shadow2DRectProjGradARB", 1, &E_GL_ARB_shader_texture_lod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// E_GL_EXT_texture_array
|
||||||
|
if (spvVersion.spv == 0) {
|
||||||
|
symbolTable.setFunctionExtensions("texture2DArray", 1, &E_GL_EXT_texture_array);
|
||||||
|
}
|
||||||
|
if (profile != EEsProfile && spvVersion.spv == 0) {
|
||||||
|
int nLodExtensions = 2;
|
||||||
|
const char *lodExtensions[] = {E_GL_EXT_texture_array, E_GL_ARB_shader_texture_lod};
|
||||||
|
|
||||||
|
if (version >= 130)
|
||||||
|
nLodExtensions = 1;
|
||||||
|
|
||||||
|
symbolTable.setFunctionExtensions("texture1DArray", 1, &E_GL_EXT_texture_array);
|
||||||
|
symbolTable.setFunctionExtensions("shadow1DArray", 1, &E_GL_EXT_texture_array);
|
||||||
|
symbolTable.setFunctionExtensions("shadow2DArray", 1, &E_GL_EXT_texture_array);
|
||||||
|
|
||||||
|
symbolTable.setFunctionExtensions("texture1DArrayLod", nLodExtensions, lodExtensions);
|
||||||
|
symbolTable.setFunctionExtensions("texture2DArrayLod", nLodExtensions, lodExtensions);
|
||||||
|
symbolTable.setFunctionExtensions("shadow1DArrayLod", nLodExtensions, lodExtensions);
|
||||||
|
symbolTable.setFunctionExtensions("shadow2DArrayLod", nLodExtensions, lodExtensions);
|
||||||
|
}
|
||||||
|
|
||||||
// E_GL_ARB_shader_image_load_store
|
// E_GL_ARB_shader_image_load_store
|
||||||
if (profile != EEsProfile && version < 420)
|
if (profile != EEsProfile && version < 420)
|
||||||
symbolTable.setFunctionExtensions("memoryBarrier", 1, &E_GL_ARB_shader_image_load_store);
|
symbolTable.setFunctionExtensions("memoryBarrier", 1, &E_GL_ARB_shader_image_load_store);
|
||||||
@@ -5555,6 +5609,15 @@ void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion
|
|||||||
symbolTable.relateToOperator("texture2DProjLod", EOpTextureProjLod);
|
symbolTable.relateToOperator("texture2DProjLod", EOpTextureProjLod);
|
||||||
symbolTable.relateToOperator("texture2DProjLodEXT", EOpTextureProjLod);
|
symbolTable.relateToOperator("texture2DProjLodEXT", EOpTextureProjLod);
|
||||||
|
|
||||||
|
symbolTable.relateToOperator("texture1DArray", EOpTexture);
|
||||||
|
symbolTable.relateToOperator("texture2DArray", EOpTexture);
|
||||||
|
symbolTable.relateToOperator("shadow1DArray", EOpTexture);
|
||||||
|
symbolTable.relateToOperator("shadow2DArray", EOpTexture);
|
||||||
|
symbolTable.relateToOperator("texture1DArrayLod", EOpTextureLod);
|
||||||
|
symbolTable.relateToOperator("texture2DArrayLod", EOpTextureLod);
|
||||||
|
symbolTable.relateToOperator("shadow1DArrayLod", EOpTextureLod);
|
||||||
|
symbolTable.relateToOperator("shadow2DArrayLod", EOpTextureLod);
|
||||||
|
|
||||||
symbolTable.relateToOperator("texture3D", EOpTexture);
|
symbolTable.relateToOperator("texture3D", EOpTexture);
|
||||||
symbolTable.relateToOperator("texture3DGradARB", EOpTextureGrad);
|
symbolTable.relateToOperator("texture3DGradARB", EOpTextureGrad);
|
||||||
symbolTable.relateToOperator("texture3DProj", EOpTextureProj);
|
symbolTable.relateToOperator("texture3DProj", EOpTextureProj);
|
||||||
|
|||||||
@@ -155,13 +155,9 @@ TIntermTyped* TIntermediate::addBinaryMath(TOperator op, TIntermTyped* left, TIn
|
|||||||
return folded;
|
return folded;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If either is a specialization constant, while the other is
|
// If can propagate spec-constantness and if the operation is an allowed
|
||||||
// a constant (or specialization constant), the result is still
|
// specialization-constant operation, make a spec-constant.
|
||||||
// a specialization constant, if the operation is an allowed
|
if (specConstantPropagates(*left, *right) && isSpecializationOperation(*node))
|
||||||
// specialization-constant operation.
|
|
||||||
if (( left->getType().getQualifier().isSpecConstant() && right->getType().getQualifier().isConstant()) ||
|
|
||||||
(right->getType().getQualifier().isSpecConstant() && left->getType().getQualifier().isConstant()))
|
|
||||||
if (isSpecializationOperation(*node))
|
|
||||||
node->getWritableType().getQualifier().makeSpecConstant();
|
node->getWritableType().getQualifier().makeSpecConstant();
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
@@ -1232,7 +1228,7 @@ TIntermAggregate* TIntermediate::makeAggregate(const TSourceLoc& loc)
|
|||||||
//
|
//
|
||||||
// Returns the selection node created.
|
// Returns the selection node created.
|
||||||
//
|
//
|
||||||
TIntermNode* TIntermediate::addSelection(TIntermTyped* cond, TIntermNodePair nodePair, const TSourceLoc& loc)
|
TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermNodePair nodePair, const TSourceLoc& loc)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// Don't prune the false path for compile-time constants; it's needed
|
// Don't prune the false path for compile-time constants; it's needed
|
||||||
@@ -1277,10 +1273,19 @@ TIntermTyped* TIntermediate::addMethod(TIntermTyped* object, const TType& type,
|
|||||||
// a true path, and a false path. The two paths are specified
|
// a true path, and a false path. The two paths are specified
|
||||||
// as separate parameters.
|
// as separate parameters.
|
||||||
//
|
//
|
||||||
|
// Specialization constant operations include
|
||||||
|
// - The ternary operator ( ? : )
|
||||||
|
//
|
||||||
// Returns the selection node created, or nullptr if one could not be.
|
// Returns the selection node created, or nullptr if one could not be.
|
||||||
//
|
//
|
||||||
TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc& loc)
|
TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc& loc)
|
||||||
{
|
{
|
||||||
|
// If it's void, go to the if-then-else selection()
|
||||||
|
if (trueBlock->getBasicType() == EbtVoid && falseBlock->getBasicType() == EbtVoid) {
|
||||||
|
TIntermNodePair pair = { trueBlock, falseBlock };
|
||||||
|
return addSelection(cond, pair, loc);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Get compatible types.
|
// Get compatible types.
|
||||||
//
|
//
|
||||||
@@ -1314,10 +1319,16 @@ TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* true
|
|||||||
// Make a selection node.
|
// Make a selection node.
|
||||||
//
|
//
|
||||||
TIntermSelection* node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType());
|
TIntermSelection* node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType());
|
||||||
node->getQualifier().makeTemporary();
|
|
||||||
node->setLoc(loc);
|
node->setLoc(loc);
|
||||||
node->getQualifier().precision = std::max(trueBlock->getQualifier().precision, falseBlock->getQualifier().precision);
|
node->getQualifier().precision = std::max(trueBlock->getQualifier().precision, falseBlock->getQualifier().precision);
|
||||||
|
|
||||||
|
if ((cond->getQualifier().isConstant() && specConstantPropagates(*trueBlock, *falseBlock)) ||
|
||||||
|
(cond->getQualifier().isSpecConstant() && trueBlock->getQualifier().isConstant() &&
|
||||||
|
falseBlock->getQualifier().isConstant()))
|
||||||
|
node->getQualifier().makeSpecConstant();
|
||||||
|
else
|
||||||
|
node->getQualifier().makeTemporary();
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1392,6 +1403,14 @@ TIntermConstantUnion* TIntermediate::addConstantUnion(double d, TBasicType baseT
|
|||||||
return addConstantUnion(unionArray, TType(baseType, EvqConst), loc, literal);
|
return addConstantUnion(unionArray, TType(baseType, EvqConst), loc, literal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TIntermConstantUnion* TIntermediate::addConstantUnion(const TString* s, const TSourceLoc& loc, bool literal) const
|
||||||
|
{
|
||||||
|
TConstUnionArray unionArray(1);
|
||||||
|
unionArray[0].setSConst(s);
|
||||||
|
|
||||||
|
return addConstantUnion(unionArray, TType(EbtString, EvqConst), loc, literal);
|
||||||
|
}
|
||||||
|
|
||||||
// Put vector swizzle selectors onto the given sequence
|
// Put vector swizzle selectors onto the given sequence
|
||||||
void TIntermediate::pushSelector(TIntermSequence& sequence, const TVectorSelector& selector, const TSourceLoc& loc)
|
void TIntermediate::pushSelector(TIntermSequence& sequence, const TVectorSelector& selector, const TSourceLoc& loc)
|
||||||
{
|
{
|
||||||
@@ -2639,4 +2658,13 @@ void TIntermAggregate::addToPragmaTable(const TPragmaTable& pTable)
|
|||||||
*pragmaTable = pTable;
|
*pragmaTable = pTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If either node is a specialization constant, while the other is
|
||||||
|
// a constant (or specialization constant), the result is still
|
||||||
|
// a specialization constant.
|
||||||
|
bool TIntermediate::specConstantPropagates(const TIntermTyped& node1, const TIntermTyped& node2)
|
||||||
|
{
|
||||||
|
return (node1.getType().getQualifier().isSpecConstant() && node2.getType().getQualifier().isConstant()) ||
|
||||||
|
(node2.getType().getQualifier().isSpecConstant() && node1.getType().getQualifier().isConstant());
|
||||||
|
}
|
||||||
|
|
||||||
} // end namespace glslang
|
} // end namespace glslang
|
||||||
|
|||||||
@@ -223,20 +223,11 @@ void TParseContextBase::rValueErrorCheck(const TSourceLoc& loc, const char* op,
|
|||||||
error(loc, "can't read from writeonly object: ", op, symNode->getName().c_str());
|
error(loc, "can't read from writeonly object: ", op, symNode->getName().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a linkage symbol node for 'symbol', which
|
|
||||||
// must have its type fully edited, as this will snapshot the type.
|
|
||||||
// It is okay if symbol becomes invalid before finish().
|
|
||||||
void TParseContextBase::trackLinkage(TSymbol& symbol)
|
|
||||||
{
|
|
||||||
if (!parsingBuiltins)
|
|
||||||
intermediate.addSymbolLinkageNode(linkage, symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add 'symbol' to the list of deferred linkage symbols, which
|
// Add 'symbol' to the list of deferred linkage symbols, which
|
||||||
// are later processed in finish(), at which point the symbol
|
// are later processed in finish(), at which point the symbol
|
||||||
// must still be valid.
|
// must still be valid.
|
||||||
// It is okay if the symbol's type will be subsequently edited.
|
// It is okay if the symbol's type will be subsequently edited.
|
||||||
void TParseContextBase::trackLinkageDeferred(TSymbol& symbol)
|
void TParseContextBase::trackLinkage(TSymbol& symbol)
|
||||||
{
|
{
|
||||||
if (!parsingBuiltins)
|
if (!parsingBuiltins)
|
||||||
linkageSymbols.push_back(&symbol);
|
linkageSymbols.push_back(&symbol);
|
||||||
@@ -253,7 +244,7 @@ void TParseContextBase::makeEditable(TSymbol*& symbol)
|
|||||||
|
|
||||||
// Save it (deferred, so it can be edited first) in the AST for linker use.
|
// Save it (deferred, so it can be edited first) in the AST for linker use.
|
||||||
if (symbol)
|
if (symbol)
|
||||||
trackLinkageDeferred(*symbol);
|
trackLinkage(*symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a writable version of the variable 'name'.
|
// Return a writable version of the variable 'name'.
|
||||||
@@ -539,7 +530,7 @@ void TParseContextBase::parseSwizzleSelector(const TSourceLoc& loc, const TStrin
|
|||||||
// Make the passed-in variable information become a member of the
|
// Make the passed-in variable information become a member of the
|
||||||
// global uniform block. If this doesn't exist yet, make it.
|
// global uniform block. If this doesn't exist yet, make it.
|
||||||
//
|
//
|
||||||
void TParseContextBase::growGlobalUniformBlock(TSourceLoc& loc, TType& memberType, TString& memberName)
|
void TParseContextBase::growGlobalUniformBlock(TSourceLoc& loc, TType& memberType, TString& memberName, TTypeList* typeList)
|
||||||
{
|
{
|
||||||
// make the global block, if not yet made
|
// make the global block, if not yet made
|
||||||
if (globalUniformBlock == nullptr) {
|
if (globalUniformBlock == nullptr) {
|
||||||
@@ -557,6 +548,8 @@ void TParseContextBase::growGlobalUniformBlock(TSourceLoc& loc, TType& memberTyp
|
|||||||
TType* type = new TType;
|
TType* type = new TType;
|
||||||
type->shallowCopy(memberType);
|
type->shallowCopy(memberType);
|
||||||
type->setFieldName(memberName);
|
type->setFieldName(memberName);
|
||||||
|
if (typeList)
|
||||||
|
type->setStruct(typeList);
|
||||||
TTypeLoc typeLoc = {type, loc};
|
TTypeLoc typeLoc = {type, loc};
|
||||||
globalUniformBlock->getType().getWritableStruct()->push_back(typeLoc);
|
globalUniformBlock->getType().getWritableStruct()->push_back(typeLoc);
|
||||||
}
|
}
|
||||||
@@ -577,7 +570,7 @@ bool TParseContextBase::insertGlobalUniformBlock()
|
|||||||
// This is the first request; we need a normal symbol table insert
|
// This is the first request; we need a normal symbol table insert
|
||||||
inserted = symbolTable.insert(*globalUniformBlock);
|
inserted = symbolTable.insert(*globalUniformBlock);
|
||||||
if (inserted)
|
if (inserted)
|
||||||
trackLinkageDeferred(*globalUniformBlock);
|
trackLinkage(*globalUniformBlock);
|
||||||
} else if (firstNewMember <= numMembers) {
|
} else if (firstNewMember <= numMembers) {
|
||||||
// This is a follow-on request; we need to amend the first insert
|
// This is a follow-on request; we need to amend the first insert
|
||||||
inserted = symbolTable.amend(*globalUniformBlock, firstNewMember);
|
inserted = symbolTable.amend(*globalUniformBlock, firstNewMember);
|
||||||
@@ -593,12 +586,14 @@ bool TParseContextBase::insertGlobalUniformBlock()
|
|||||||
|
|
||||||
void TParseContextBase::finish()
|
void TParseContextBase::finish()
|
||||||
{
|
{
|
||||||
if (!parsingBuiltins) {
|
if (parsingBuiltins)
|
||||||
|
return;
|
||||||
|
|
||||||
// Transfer the linkage symbols to AST nodes
|
// Transfer the linkage symbols to AST nodes
|
||||||
|
TIntermAggregate* linkage = new TIntermAggregate;
|
||||||
for (auto i = linkageSymbols.begin(); i != linkageSymbols.end(); ++i)
|
for (auto i = linkageSymbols.begin(); i != linkageSymbols.end(); ++i)
|
||||||
intermediate.addSymbolLinkageNode(linkage, **i);
|
intermediate.addSymbolLinkageNode(linkage, **i);
|
||||||
intermediate.addSymbolLinkageNodes(linkage, getLanguage(), symbolTable);
|
intermediate.addSymbolLinkageNodes(linkage, getLanguage(), symbolTable);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namespace glslang
|
} // end namespace glslang
|
||||||
|
|||||||
@@ -3009,7 +3009,7 @@ void TParseContext::declareArray(const TSourceLoc& loc, TString& identifier, con
|
|||||||
symbol = new TVariable(&identifier, type);
|
symbol = new TVariable(&identifier, type);
|
||||||
symbolTable.insert(*symbol);
|
symbolTable.insert(*symbol);
|
||||||
if (symbolTable.atGlobalLevel())
|
if (symbolTable.atGlobalLevel())
|
||||||
trackLinkageDeferred(*symbol);
|
trackLinkage(*symbol);
|
||||||
|
|
||||||
if (! symbolTable.atBuiltInLevel()) {
|
if (! symbolTable.atBuiltInLevel()) {
|
||||||
if (isIoResizeArray(type)) {
|
if (isIoResizeArray(type)) {
|
||||||
@@ -3431,9 +3431,9 @@ void TParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& newT
|
|||||||
oldType.getQualifier().layoutViewportRelative = newType.getQualifier().layoutViewportRelative;
|
oldType.getQualifier().layoutViewportRelative = newType.getQualifier().layoutViewportRelative;
|
||||||
oldType.getQualifier().layoutSecondaryViewportRelativeOffset = newType.getQualifier().layoutSecondaryViewportRelativeOffset;
|
oldType.getQualifier().layoutSecondaryViewportRelativeOffset = newType.getQualifier().layoutSecondaryViewportRelativeOffset;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if (oldType.isImplicitlySizedArray() && newType.isExplicitlySizedArray())
|
if (oldType.isImplicitlySizedArray() && newType.isExplicitlySizedArray())
|
||||||
oldType.changeOuterArraySize(newType.getOuterArraySize());
|
oldType.changeOuterArraySize(newType.getOuterArraySize());
|
||||||
#endif
|
|
||||||
|
|
||||||
// go to next member
|
// go to next member
|
||||||
++member;
|
++member;
|
||||||
@@ -3476,7 +3476,7 @@ void TParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& newT
|
|||||||
fixIoArraySize(loc, block->getWritableType());
|
fixIoArraySize(loc, block->getWritableType());
|
||||||
|
|
||||||
// Save it in the AST for linker use.
|
// Save it in the AST for linker use.
|
||||||
trackLinkageDeferred(*block);
|
trackLinkage(*block);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TParseContext::paramCheckFix(const TSourceLoc& loc, const TStorageQualifier& qualifier, TType& type)
|
void TParseContext::paramCheckFix(const TSourceLoc& loc, const TStorageQualifier& qualifier, TType& type)
|
||||||
@@ -3958,9 +3958,7 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi
|
|||||||
publicType.shaderQualifiers.layoutOverrideCoverage = true;
|
publicType.shaderQualifiers.layoutOverrideCoverage = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#ifdef NV_EXTENSIONS
|
|
||||||
if (language == EShLangVertex ||
|
if (language == EShLangVertex ||
|
||||||
language == EShLangTessControl ||
|
language == EShLangTessControl ||
|
||||||
language == EShLangTessEvaluation ||
|
language == EShLangTessEvaluation ||
|
||||||
@@ -3971,6 +3969,8 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), "");
|
error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), "");
|
||||||
}
|
}
|
||||||
@@ -4008,16 +4008,20 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi
|
|||||||
// - uniform offsets
|
// - uniform offsets
|
||||||
// - atomic_uint offsets
|
// - atomic_uint offsets
|
||||||
const char* feature = "offset";
|
const char* feature = "offset";
|
||||||
|
if (spvVersion.spv == 0) {
|
||||||
requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, feature);
|
requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, feature);
|
||||||
const char* exts[2] = { E_GL_ARB_enhanced_layouts, E_GL_ARB_shader_atomic_counters };
|
const char* exts[2] = { E_GL_ARB_enhanced_layouts, E_GL_ARB_shader_atomic_counters };
|
||||||
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 420, 2, exts, feature);
|
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 420, 2, exts, feature);
|
||||||
profileRequires(loc, EEsProfile, 310, nullptr, feature);
|
profileRequires(loc, EEsProfile, 310, nullptr, feature);
|
||||||
|
}
|
||||||
publicType.qualifier.layoutOffset = value;
|
publicType.qualifier.layoutOffset = value;
|
||||||
return;
|
return;
|
||||||
} else if (id == "align") {
|
} else if (id == "align") {
|
||||||
const char* feature = "uniform buffer-member align";
|
const char* feature = "uniform buffer-member align";
|
||||||
|
if (spvVersion.spv == 0) {
|
||||||
requireProfile(loc, ECoreProfile | ECompatibilityProfile, feature);
|
requireProfile(loc, ECoreProfile | ECompatibilityProfile, feature);
|
||||||
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, feature);
|
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, feature);
|
||||||
|
}
|
||||||
// "The specified alignment must be a power of 2, or a compile-time error results."
|
// "The specified alignment must be a power of 2, or a compile-time error results."
|
||||||
if (! IsPow2(value))
|
if (! IsPow2(value))
|
||||||
error(loc, "must be a power of 2", "align", "");
|
error(loc, "must be a power of 2", "align", "");
|
||||||
@@ -4495,8 +4499,11 @@ void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (type.isImage() && ! qualifier.writeonly)
|
} else if (type.isImage() && ! qualifier.writeonly) {
|
||||||
error(loc, "image variables not declared 'writeonly' must have a format layout qualifier", "", "");
|
const char *explanation = "image variables declared 'writeonly' without a format layout qualifier";
|
||||||
|
requireProfile(loc, ECoreProfile | ECompatibilityProfile, explanation);
|
||||||
|
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 0, E_GL_EXT_shader_image_load_formatted, explanation);
|
||||||
|
}
|
||||||
|
|
||||||
if (qualifier.layoutPushConstant && type.getBasicType() != EbtBlock)
|
if (qualifier.layoutPushConstant && type.getBasicType() != EbtBlock)
|
||||||
error(loc, "can only be used with a block", "push_constant", "");
|
error(loc, "can only be used with a block", "push_constant", "");
|
||||||
@@ -5056,7 +5063,7 @@ TVariable* TParseContext::declareNonArray(const TSourceLoc& loc, TString& identi
|
|||||||
// add variable to symbol table
|
// add variable to symbol table
|
||||||
if (symbolTable.insert(*variable)) {
|
if (symbolTable.insert(*variable)) {
|
||||||
if (symbolTable.atGlobalLevel())
|
if (symbolTable.atGlobalLevel())
|
||||||
trackLinkageDeferred(*variable);
|
trackLinkage(*variable);
|
||||||
return variable;
|
return variable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5546,9 +5553,11 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con
|
|||||||
if (memberType.isArray())
|
if (memberType.isArray())
|
||||||
arrayUnsizedCheck(memberLoc, currentBlockQualifier, &memberType.getArraySizes(), false, member == typeList.size() - 1);
|
arrayUnsizedCheck(memberLoc, currentBlockQualifier, &memberType.getArraySizes(), false, member == typeList.size() - 1);
|
||||||
if (memberQualifier.hasOffset()) {
|
if (memberQualifier.hasOffset()) {
|
||||||
|
if (spvVersion.spv == 0) {
|
||||||
requireProfile(memberLoc, ~EEsProfile, "offset on block member");
|
requireProfile(memberLoc, ~EEsProfile, "offset on block member");
|
||||||
profileRequires(memberLoc, ~EEsProfile, 440, E_GL_ARB_enhanced_layouts, "offset on block member");
|
profileRequires(memberLoc, ~EEsProfile, 440, E_GL_ARB_enhanced_layouts, "offset on block member");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (memberType.containsOpaque())
|
if (memberType.containsOpaque())
|
||||||
error(memberLoc, "member of block cannot be or contain a sampler, image, or atomic_uint type", typeList[member].type->getFieldName().c_str(), "");
|
error(memberLoc, "member of block cannot be or contain a sampler, image, or atomic_uint type", typeList[member].type->getFieldName().c_str(), "");
|
||||||
@@ -5718,7 +5727,7 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con
|
|||||||
fixIoArraySize(loc, variable.getWritableType());
|
fixIoArraySize(loc, variable.getWritableType());
|
||||||
|
|
||||||
// Save it in the AST for linker use.
|
// Save it in the AST for linker use.
|
||||||
trackLinkageDeferred(variable);
|
trackLinkage(variable);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do all block-declaration checking regarding the combination of in/out/uniform/buffer
|
// Do all block-declaration checking regarding the combination of in/out/uniform/buffer
|
||||||
|
|||||||
@@ -79,9 +79,7 @@ public:
|
|||||||
symbolTable(symbolTable),
|
symbolTable(symbolTable),
|
||||||
parsingBuiltins(parsingBuiltins), scanContext(nullptr), ppContext(nullptr),
|
parsingBuiltins(parsingBuiltins), scanContext(nullptr), ppContext(nullptr),
|
||||||
globalUniformBlock(nullptr)
|
globalUniformBlock(nullptr)
|
||||||
{
|
{ }
|
||||||
linkage = new TIntermAggregate;
|
|
||||||
}
|
|
||||||
virtual ~TParseContextBase() { }
|
virtual ~TParseContextBase() { }
|
||||||
|
|
||||||
virtual void C_DECL error(const TSourceLoc&, const char* szReason, const char* szToken,
|
virtual void C_DECL error(const TSourceLoc&, const char* szReason, const char* szToken,
|
||||||
@@ -141,7 +139,7 @@ public:
|
|||||||
// TODO: This could perhaps get its own object, but the current design doesn't work
|
// TODO: This could perhaps get its own object, but the current design doesn't work
|
||||||
// yet when new uniform variables are declared between function definitions, so
|
// yet when new uniform variables are declared between function definitions, so
|
||||||
// this is pending getting a fully functional design.
|
// this is pending getting a fully functional design.
|
||||||
virtual void growGlobalUniformBlock(TSourceLoc&, TType&, TString& memberName);
|
virtual void growGlobalUniformBlock(TSourceLoc&, TType&, TString& memberName, TTypeList* typeList = nullptr);
|
||||||
virtual bool insertGlobalUniformBlock();
|
virtual bool insertGlobalUniformBlock();
|
||||||
|
|
||||||
virtual bool lValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*);
|
virtual bool lValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*);
|
||||||
@@ -183,13 +181,9 @@ protected:
|
|||||||
const char* szExtraInfoFormat, TPrefixType prefix,
|
const char* szExtraInfoFormat, TPrefixType prefix,
|
||||||
va_list args);
|
va_list args);
|
||||||
virtual void trackLinkage(TSymbol& symbol);
|
virtual void trackLinkage(TSymbol& symbol);
|
||||||
virtual void trackLinkageDeferred(TSymbol& symbol);
|
|
||||||
virtual void makeEditable(TSymbol*&);
|
virtual void makeEditable(TSymbol*&);
|
||||||
virtual TVariable* getEditableVariable(const char* name);
|
virtual TVariable* getEditableVariable(const char* name);
|
||||||
virtual void finish();
|
virtual void finish();
|
||||||
|
|
||||||
private:
|
|
||||||
TIntermAggregate* linkage;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -105,8 +105,8 @@ void SetThreadPoolAllocator(TPoolAllocator& poolAllocator)
|
|||||||
TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) :
|
TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) :
|
||||||
pageSize(growthIncrement),
|
pageSize(growthIncrement),
|
||||||
alignment(allocationAlignment),
|
alignment(allocationAlignment),
|
||||||
freeList(0),
|
freeList(nullptr),
|
||||||
inUseList(0),
|
inUseList(nullptr),
|
||||||
numCalls(0)
|
numCalls(0)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1006,7 +1006,6 @@ int TScanContext::tokenizeIdentifier()
|
|||||||
|
|
||||||
case ISAMPLER1D:
|
case ISAMPLER1D:
|
||||||
case ISAMPLER1DARRAY:
|
case ISAMPLER1DARRAY:
|
||||||
case SAMPLER1DARRAYSHADOW:
|
|
||||||
case USAMPLER1D:
|
case USAMPLER1D:
|
||||||
case USAMPLER1DARRAY:
|
case USAMPLER1DARRAY:
|
||||||
afterType = true;
|
afterType = true;
|
||||||
@@ -1017,8 +1016,6 @@ int TScanContext::tokenizeIdentifier()
|
|||||||
case UVEC3:
|
case UVEC3:
|
||||||
case UVEC4:
|
case UVEC4:
|
||||||
case SAMPLERCUBESHADOW:
|
case SAMPLERCUBESHADOW:
|
||||||
case SAMPLER2DARRAY:
|
|
||||||
case SAMPLER2DARRAYSHADOW:
|
|
||||||
case ISAMPLER2D:
|
case ISAMPLER2D:
|
||||||
case ISAMPLER3D:
|
case ISAMPLER3D:
|
||||||
case ISAMPLERCUBE:
|
case ISAMPLERCUBE:
|
||||||
@@ -1085,6 +1082,37 @@ int TScanContext::tokenizeIdentifier()
|
|||||||
reservedWord();
|
reservedWord();
|
||||||
return keyword;
|
return keyword;
|
||||||
|
|
||||||
|
case SAMPLER1DARRAY:
|
||||||
|
case SAMPLER1DARRAYSHADOW:
|
||||||
|
afterType = true;
|
||||||
|
if (parseContext.symbolTable.atBuiltInLevel())
|
||||||
|
return keyword;
|
||||||
|
else if (parseContext.profile == EEsProfile) {
|
||||||
|
if (parseContext.version >= 300)
|
||||||
|
reservedWord();
|
||||||
|
else
|
||||||
|
return identifierOrType();
|
||||||
|
} else if (parseContext.version < 130 && !parseContext.extensionTurnedOn(E_GL_EXT_texture_array)) {
|
||||||
|
if (parseContext.forwardCompatible)
|
||||||
|
parseContext.warn(loc, "using future keyword", tokenText, "");
|
||||||
|
|
||||||
|
return identifierOrType();
|
||||||
|
}
|
||||||
|
return keyword;
|
||||||
|
|
||||||
|
case SAMPLER2DARRAY:
|
||||||
|
case SAMPLER2DARRAYSHADOW:
|
||||||
|
afterType = true;
|
||||||
|
if (parseContext.symbolTable.atBuiltInLevel())
|
||||||
|
return keyword;
|
||||||
|
else if (parseContext.version < 130 && ((parseContext.profile == EEsProfile && keyword != SAMPLER2DARRAY) || !parseContext.extensionTurnedOn(E_GL_EXT_texture_array))) {
|
||||||
|
if (parseContext.forwardCompatible)
|
||||||
|
parseContext.warn(loc, "using future keyword", tokenText, "");
|
||||||
|
|
||||||
|
return identifierOrType();
|
||||||
|
}
|
||||||
|
return keyword;
|
||||||
|
|
||||||
case SAMPLER2DRECT:
|
case SAMPLER2DRECT:
|
||||||
case SAMPLER2DRECTSHADOW:
|
case SAMPLER2DRECTSHADOW:
|
||||||
afterType = true;
|
afterType = true;
|
||||||
@@ -1098,15 +1126,6 @@ int TScanContext::tokenizeIdentifier()
|
|||||||
}
|
}
|
||||||
return keyword;
|
return keyword;
|
||||||
|
|
||||||
case SAMPLER1DARRAY:
|
|
||||||
afterType = true;
|
|
||||||
if (parseContext.profile == EEsProfile && parseContext.version == 300)
|
|
||||||
reservedWord();
|
|
||||||
else if ((parseContext.profile == EEsProfile && parseContext.version < 300) ||
|
|
||||||
(parseContext.profile != EEsProfile && parseContext.version < 130))
|
|
||||||
return identifierOrType();
|
|
||||||
return keyword;
|
|
||||||
|
|
||||||
case SAMPLEREXTERNALOES:
|
case SAMPLEREXTERNALOES:
|
||||||
afterType = true;
|
afterType = true;
|
||||||
if (parseContext.symbolTable.atBuiltInLevel() || parseContext.extensionTurnedOn(E_GL_OES_EGL_image_external))
|
if (parseContext.symbolTable.atBuiltInLevel() || parseContext.extensionTurnedOn(E_GL_OES_EGL_image_external))
|
||||||
|
|||||||
@@ -1632,6 +1632,7 @@ TProgram::TProgram() : pool(0), reflection(0), ioMapper(nullptr), linked(false)
|
|||||||
|
|
||||||
TProgram::~TProgram()
|
TProgram::~TProgram()
|
||||||
{
|
{
|
||||||
|
delete ioMapper;
|
||||||
delete infoSink;
|
delete infoSink;
|
||||||
delete reflection;
|
delete reflection;
|
||||||
|
|
||||||
@@ -1707,6 +1708,15 @@ bool TProgram::linkStage(EShLanguage stage, EShMessages messages)
|
|||||||
intermediate[stage] = new TIntermediate(stage,
|
intermediate[stage] = new TIntermediate(stage,
|
||||||
firstIntermediate->getVersion(),
|
firstIntermediate->getVersion(),
|
||||||
firstIntermediate->getProfile());
|
firstIntermediate->getProfile());
|
||||||
|
|
||||||
|
|
||||||
|
// The new TIntermediate must use the same origin as the original TIntermediates.
|
||||||
|
// Otherwise linking will fail due to different coordinate systems.
|
||||||
|
if (firstIntermediate->getOriginUpperLeft()) {
|
||||||
|
intermediate[stage]->setOriginUpperLeft();
|
||||||
|
}
|
||||||
|
intermediate[stage]->setSpv(firstIntermediate->getSpv());
|
||||||
|
|
||||||
newedIntermediate[stage] = true;
|
newedIntermediate[stage] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,12 @@ public:
|
|||||||
|
|
||||||
virtual const TString& getName() const { return *name; }
|
virtual const TString& getName() const { return *name; }
|
||||||
virtual void changeName(const TString* newName) { name = newName; }
|
virtual void changeName(const TString* newName) { name = newName; }
|
||||||
|
virtual void addPrefix(const char* prefix)
|
||||||
|
{
|
||||||
|
TString newName(prefix);
|
||||||
|
newName.append(*name);
|
||||||
|
changeName(NewPoolTString(newName.c_str()));
|
||||||
|
}
|
||||||
virtual const TString& getMangledName() const { return getName(); }
|
virtual const TString& getMangledName() const { return getName(); }
|
||||||
virtual TFunction* getAsFunction() { return 0; }
|
virtual TFunction* getAsFunction() { return 0; }
|
||||||
virtual const TFunction* getAsFunction() const { return 0; }
|
virtual const TFunction* getAsFunction() const { return 0; }
|
||||||
@@ -192,6 +198,7 @@ struct TParameter {
|
|||||||
TString *name;
|
TString *name;
|
||||||
TType* type;
|
TType* type;
|
||||||
TIntermTyped* defaultValue;
|
TIntermTyped* defaultValue;
|
||||||
|
TBuiltInVariable declaredBuiltIn;
|
||||||
void copyParam(const TParameter& param)
|
void copyParam(const TParameter& param)
|
||||||
{
|
{
|
||||||
if (param.name)
|
if (param.name)
|
||||||
@@ -200,6 +207,7 @@ struct TParameter {
|
|||||||
name = 0;
|
name = 0;
|
||||||
type = param.type->clone();
|
type = param.type->clone();
|
||||||
defaultValue = param.defaultValue;
|
defaultValue = param.defaultValue;
|
||||||
|
declaredBuiltIn = param.declaredBuiltIn;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -216,7 +224,11 @@ public:
|
|||||||
TSymbol(name),
|
TSymbol(name),
|
||||||
mangledName(*name + '('),
|
mangledName(*name + '('),
|
||||||
op(tOp),
|
op(tOp),
|
||||||
defined(false), prototyped(false), defaultParamCount(0) { returnType.shallowCopy(retType); }
|
defined(false), prototyped(false), defaultParamCount(0)
|
||||||
|
{
|
||||||
|
returnType.shallowCopy(retType);
|
||||||
|
declaredBuiltIn = retType.getQualifier().builtIn;
|
||||||
|
}
|
||||||
virtual TFunction* clone() const;
|
virtual TFunction* clone() const;
|
||||||
virtual ~TFunction();
|
virtual ~TFunction();
|
||||||
|
|
||||||
@@ -226,15 +238,22 @@ public:
|
|||||||
virtual void addParameter(TParameter& p)
|
virtual void addParameter(TParameter& p)
|
||||||
{
|
{
|
||||||
assert(writable);
|
assert(writable);
|
||||||
|
p.declaredBuiltIn = p.type->getQualifier().builtIn;
|
||||||
parameters.push_back(p);
|
parameters.push_back(p);
|
||||||
p.type->appendMangledName(mangledName);
|
p.type->appendMangledName(mangledName);
|
||||||
|
|
||||||
if (p.defaultValue != nullptr)
|
if (p.defaultValue != nullptr)
|
||||||
defaultParamCount++;
|
defaultParamCount++;
|
||||||
}
|
}
|
||||||
|
virtual void addPrefix(const char* prefix) override
|
||||||
|
{
|
||||||
|
TSymbol::addPrefix(prefix);
|
||||||
|
mangledName.insert(0, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
virtual const TString& getMangledName() const { return mangledName; }
|
virtual const TString& getMangledName() const { return mangledName; }
|
||||||
virtual const TType& getType() const { return returnType; }
|
virtual const TType& getType() const { return returnType; }
|
||||||
|
virtual TBuiltInVariable getDeclaredBuiltInType() const { return declaredBuiltIn; }
|
||||||
virtual TType& getWritableType() { return returnType; }
|
virtual TType& getWritableType() { return returnType; }
|
||||||
virtual void relateToOperator(TOperator o) { assert(writable); op = o; }
|
virtual void relateToOperator(TOperator o) { assert(writable); op = o; }
|
||||||
virtual TOperator getBuiltInOp() const { return op; }
|
virtual TOperator getBuiltInOp() const { return op; }
|
||||||
@@ -262,6 +281,8 @@ protected:
|
|||||||
typedef TVector<TParameter> TParamList;
|
typedef TVector<TParameter> TParamList;
|
||||||
TParamList parameters;
|
TParamList parameters;
|
||||||
TType returnType;
|
TType returnType;
|
||||||
|
TBuiltInVariable declaredBuiltIn;
|
||||||
|
|
||||||
TString mangledName;
|
TString mangledName;
|
||||||
TOperator op;
|
TOperator op;
|
||||||
bool defined;
|
bool defined;
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ void TParseVersions::initializeExtensionBehavior()
|
|||||||
extensionBehavior[E_GL_OES_EGL_image_external] = EBhDisable;
|
extensionBehavior[E_GL_OES_EGL_image_external] = EBhDisable;
|
||||||
extensionBehavior[E_GL_EXT_shader_texture_lod] = EBhDisable;
|
extensionBehavior[E_GL_EXT_shader_texture_lod] = EBhDisable;
|
||||||
|
|
||||||
|
extensionBehavior[E_GL_EXT_texture_array] = EBhDisable;
|
||||||
extensionBehavior[E_GL_ARB_texture_rectangle] = EBhDisable;
|
extensionBehavior[E_GL_ARB_texture_rectangle] = EBhDisable;
|
||||||
extensionBehavior[E_GL_3DL_array_objects] = EBhDisable;
|
extensionBehavior[E_GL_3DL_array_objects] = EBhDisable;
|
||||||
extensionBehavior[E_GL_ARB_shading_language_420pack] = EBhDisable;
|
extensionBehavior[E_GL_ARB_shading_language_420pack] = EBhDisable;
|
||||||
@@ -182,6 +183,7 @@ void TParseVersions::initializeExtensionBehavior()
|
|||||||
// extensionBehavior[E_GL_ARB_cull_distance] = EBhDisable; // present for 4.5, but need extension control over block members
|
// extensionBehavior[E_GL_ARB_cull_distance] = EBhDisable; // present for 4.5, but need extension control over block members
|
||||||
|
|
||||||
extensionBehavior[E_GL_EXT_shader_non_constant_global_initializers] = EBhDisable;
|
extensionBehavior[E_GL_EXT_shader_non_constant_global_initializers] = EBhDisable;
|
||||||
|
extensionBehavior[E_GL_EXT_shader_image_load_formatted] = EBhDisable;
|
||||||
|
|
||||||
// #line and #include
|
// #line and #include
|
||||||
extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhDisable;
|
extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhDisable;
|
||||||
@@ -201,6 +203,7 @@ void TParseVersions::initializeExtensionBehavior()
|
|||||||
extensionBehavior[E_GL_ARB_shader_viewport_layer_array] = EBhDisable;
|
extensionBehavior[E_GL_ARB_shader_viewport_layer_array] = EBhDisable;
|
||||||
extensionBehavior[E_GL_NV_viewport_array2] = EBhDisable;
|
extensionBehavior[E_GL_NV_viewport_array2] = EBhDisable;
|
||||||
extensionBehavior[E_GL_NV_stereo_view_rendering] = EBhDisable;
|
extensionBehavior[E_GL_NV_stereo_view_rendering] = EBhDisable;
|
||||||
|
extensionBehavior[E_GL_NVX_multiview_per_view_attributes] = EBhDisable;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// AEP
|
// AEP
|
||||||
@@ -240,6 +243,7 @@ void TParseVersions::getPreamble(std::string& preamble)
|
|||||||
preamble =
|
preamble =
|
||||||
"#define GL_ES 1\n"
|
"#define GL_ES 1\n"
|
||||||
"#define GL_FRAGMENT_PRECISION_HIGH 1\n"
|
"#define GL_FRAGMENT_PRECISION_HIGH 1\n"
|
||||||
|
"#define GL_EXT_texture_array 1\n"
|
||||||
"#define GL_OES_texture_3D 1\n"
|
"#define GL_OES_texture_3D 1\n"
|
||||||
"#define GL_OES_standard_derivatives 1\n"
|
"#define GL_OES_standard_derivatives 1\n"
|
||||||
"#define GL_EXT_frag_depth 1\n"
|
"#define GL_EXT_frag_depth 1\n"
|
||||||
@@ -278,6 +282,7 @@ void TParseVersions::getPreamble(std::string& preamble)
|
|||||||
} else {
|
} else {
|
||||||
preamble =
|
preamble =
|
||||||
"#define GL_FRAGMENT_PRECISION_HIGH 1\n"
|
"#define GL_FRAGMENT_PRECISION_HIGH 1\n"
|
||||||
|
"#define GL_EXT_texture_array 1\n"
|
||||||
"#define GL_ARB_texture_rectangle 1\n"
|
"#define GL_ARB_texture_rectangle 1\n"
|
||||||
"#define GL_ARB_shading_language_420pack 1\n"
|
"#define GL_ARB_shading_language_420pack 1\n"
|
||||||
"#define GL_ARB_texture_gather 1\n"
|
"#define GL_ARB_texture_gather 1\n"
|
||||||
@@ -302,6 +307,7 @@ void TParseVersions::getPreamble(std::string& preamble)
|
|||||||
"#define GL_ARB_sparse_texture_clamp 1\n"
|
"#define GL_ARB_sparse_texture_clamp 1\n"
|
||||||
// "#define GL_ARB_cull_distance 1\n" // present for 4.5, but need extension control over block members
|
// "#define GL_ARB_cull_distance 1\n" // present for 4.5, but need extension control over block members
|
||||||
"#define GL_EXT_shader_non_constant_global_initializers 1\n"
|
"#define GL_EXT_shader_non_constant_global_initializers 1\n"
|
||||||
|
"#define GL_EXT_shader_image_load_formatted 1\n"
|
||||||
|
|
||||||
#ifdef AMD_EXTENSIONS
|
#ifdef AMD_EXTENSIONS
|
||||||
"#define GL_AMD_shader_ballot 1\n"
|
"#define GL_AMD_shader_ballot 1\n"
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ const char* const E_GL_EXT_frag_depth = "GL_EXT_frag_depth";
|
|||||||
const char* const E_GL_OES_EGL_image_external = "GL_OES_EGL_image_external";
|
const char* const E_GL_OES_EGL_image_external = "GL_OES_EGL_image_external";
|
||||||
const char* const E_GL_EXT_shader_texture_lod = "GL_EXT_shader_texture_lod";
|
const char* const E_GL_EXT_shader_texture_lod = "GL_EXT_shader_texture_lod";
|
||||||
|
|
||||||
|
const char* const E_GL_EXT_texture_array = "GL_EXT_texture_array";
|
||||||
const char* const E_GL_ARB_texture_rectangle = "GL_ARB_texture_rectangle";
|
const char* const E_GL_ARB_texture_rectangle = "GL_ARB_texture_rectangle";
|
||||||
const char* const E_GL_3DL_array_objects = "GL_3DL_array_objects";
|
const char* const E_GL_3DL_array_objects = "GL_3DL_array_objects";
|
||||||
const char* const E_GL_ARB_shading_language_420pack = "GL_ARB_shading_language_420pack";
|
const char* const E_GL_ARB_shading_language_420pack = "GL_ARB_shading_language_420pack";
|
||||||
@@ -130,6 +131,7 @@ const char* const E_GL_ARB_sparse_texture_clamp = "GL_ARB_sparse_texture
|
|||||||
// const char* const E_GL_ARB_cull_distance = "GL_ARB_cull_distance"; // present for 4.5, but need extension control over block members
|
// const char* const E_GL_ARB_cull_distance = "GL_ARB_cull_distance"; // present for 4.5, but need extension control over block members
|
||||||
|
|
||||||
const char* const E_GL_EXT_shader_non_constant_global_initializers = "GL_EXT_shader_non_constant_global_initializers";
|
const char* const E_GL_EXT_shader_non_constant_global_initializers = "GL_EXT_shader_non_constant_global_initializers";
|
||||||
|
const char* const E_GL_EXT_shader_image_load_formatted = "GL_EXT_shader_image_load_formatted";
|
||||||
|
|
||||||
// #line and #include
|
// #line and #include
|
||||||
const char* const E_GL_GOOGLE_cpp_style_line_directive = "GL_GOOGLE_cpp_style_line_directive";
|
const char* const E_GL_GOOGLE_cpp_style_line_directive = "GL_GOOGLE_cpp_style_line_directive";
|
||||||
@@ -149,6 +151,7 @@ const char* const E_SPV_NV_geometry_shader_passthrough = "GL_NV_geometr
|
|||||||
const char* const E_GL_ARB_shader_viewport_layer_array = "GL_ARB_shader_viewport_layer_array";
|
const char* const E_GL_ARB_shader_viewport_layer_array = "GL_ARB_shader_viewport_layer_array";
|
||||||
const char* const E_GL_NV_viewport_array2 = "GL_NV_viewport_array2";
|
const char* const E_GL_NV_viewport_array2 = "GL_NV_viewport_array2";
|
||||||
const char* const E_GL_NV_stereo_view_rendering = "GL_NV_stereo_view_rendering";
|
const char* const E_GL_NV_stereo_view_rendering = "GL_NV_stereo_view_rendering";
|
||||||
|
const char* const E_GL_NVX_multiview_per_view_attributes = "GL_NVX_multiview_per_view_attributes";
|
||||||
|
|
||||||
// Arrays of extensions for the above viewportEXTs duplications
|
// Arrays of extensions for the above viewportEXTs duplications
|
||||||
|
|
||||||
|
|||||||
@@ -209,6 +209,9 @@ struct TResolverAdaptor
|
|||||||
TIoMapResolver& resolver;
|
TIoMapResolver& resolver;
|
||||||
TInfoSink& infoSink;
|
TInfoSink& infoSink;
|
||||||
bool& error;
|
bool& error;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TResolverAdaptor& operator=(TResolverAdaptor&);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ public:
|
|||||||
TIntermAggregate* makeAggregate(const TSourceLoc&);
|
TIntermAggregate* makeAggregate(const TSourceLoc&);
|
||||||
TIntermTyped* setAggregateOperator(TIntermNode*, TOperator, const TType& type, TSourceLoc);
|
TIntermTyped* setAggregateOperator(TIntermNode*, TOperator, const TType& type, TSourceLoc);
|
||||||
bool areAllChildConst(TIntermAggregate* aggrNode);
|
bool areAllChildConst(TIntermAggregate* aggrNode);
|
||||||
TIntermNode* addSelection(TIntermTyped* cond, TIntermNodePair code, const TSourceLoc&);
|
TIntermTyped* addSelection(TIntermTyped* cond, TIntermNodePair code, const TSourceLoc&);
|
||||||
TIntermTyped* addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc&);
|
TIntermTyped* addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc&);
|
||||||
TIntermTyped* addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc&);
|
TIntermTyped* addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc&);
|
||||||
TIntermTyped* addMethod(TIntermTyped*, const TType&, const TString*, const TSourceLoc&);
|
TIntermTyped* addMethod(TIntermTyped*, const TType&, const TString*, const TSourceLoc&);
|
||||||
@@ -263,6 +263,7 @@ public:
|
|||||||
TIntermConstantUnion* addConstantUnion(unsigned long long, const TSourceLoc&, bool literal = false) const;
|
TIntermConstantUnion* addConstantUnion(unsigned long long, const TSourceLoc&, bool literal = false) const;
|
||||||
TIntermConstantUnion* addConstantUnion(bool, const TSourceLoc&, bool literal = false) const;
|
TIntermConstantUnion* addConstantUnion(bool, const TSourceLoc&, bool literal = false) const;
|
||||||
TIntermConstantUnion* addConstantUnion(double, TBasicType, const TSourceLoc&, bool literal = false) const;
|
TIntermConstantUnion* addConstantUnion(double, TBasicType, const TSourceLoc&, bool literal = false) const;
|
||||||
|
TIntermConstantUnion* addConstantUnion(const TString*, const TSourceLoc&, bool literal = false) const;
|
||||||
TIntermTyped* promoteConstantUnion(TBasicType, TIntermConstantUnion*) const;
|
TIntermTyped* promoteConstantUnion(TBasicType, TIntermConstantUnion*) const;
|
||||||
bool parseConstTree(TIntermNode*, TConstUnionArray, TOperator, const TType&, bool singleConstantParam = false);
|
bool parseConstTree(TIntermNode*, TConstUnionArray, TOperator, const TType&, bool singleConstantParam = false);
|
||||||
TIntermLoop* addLoop(TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, const TSourceLoc&);
|
TIntermLoop* addLoop(TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, const TSourceLoc&);
|
||||||
@@ -440,6 +441,7 @@ protected:
|
|||||||
bool promoteAggregate(TIntermAggregate&);
|
bool promoteAggregate(TIntermAggregate&);
|
||||||
void pushSelector(TIntermSequence&, const TVectorSelector&, const TSourceLoc&);
|
void pushSelector(TIntermSequence&, const TVectorSelector&, const TSourceLoc&);
|
||||||
void pushSelector(TIntermSequence&, const TMatrixSelector&, const TSourceLoc&);
|
void pushSelector(TIntermSequence&, const TMatrixSelector&, const TSourceLoc&);
|
||||||
|
bool specConstantPropagates(const TIntermTyped&, const TIntermTyped&);
|
||||||
|
|
||||||
const EShLanguage language; // stage, known at construction time
|
const EShLanguage language; // stage, known at construction time
|
||||||
EShSource source; // source language, known a bit later
|
EShSource source; // source language, known a bit later
|
||||||
|
|||||||
@@ -148,10 +148,10 @@ int TPpContext::CPPdefine(TPpToken* ppToken)
|
|||||||
// record the definition of the macro
|
// record the definition of the macro
|
||||||
TSourceLoc defineLoc = ppToken->loc; // because ppToken is going to go to the next line before we report errors
|
TSourceLoc defineLoc = ppToken->loc; // because ppToken is going to go to the next line before we report errors
|
||||||
while (token != '\n' && token != EndOfInput) {
|
while (token != '\n' && token != EndOfInput) {
|
||||||
RecordToken(mac.body, token, ppToken);
|
mac.body.putToken(token, ppToken);
|
||||||
token = scanToken(ppToken);
|
token = scanToken(ppToken);
|
||||||
if (token != '\n' && ppToken->space)
|
if (token != '\n' && ppToken->space)
|
||||||
RecordToken(mac.body, ' ', ppToken);
|
mac.body.putToken(' ', ppToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for duplicate definition
|
// check for duplicate definition
|
||||||
@@ -166,15 +166,15 @@ int TPpContext::CPPdefine(TPpToken* ppToken)
|
|||||||
else {
|
else {
|
||||||
if (existing->args != mac.args)
|
if (existing->args != mac.args)
|
||||||
parseContext.ppError(defineLoc, "Macro redefined; different argument names:", "#define", atomStrings.getString(defAtom));
|
parseContext.ppError(defineLoc, "Macro redefined; different argument names:", "#define", atomStrings.getString(defAtom));
|
||||||
RewindTokenStream(existing->body);
|
existing->body.reset();
|
||||||
RewindTokenStream(mac.body);
|
mac.body.reset();
|
||||||
int newToken;
|
int newToken;
|
||||||
do {
|
do {
|
||||||
int oldToken;
|
int oldToken;
|
||||||
TPpToken oldPpToken;
|
TPpToken oldPpToken;
|
||||||
TPpToken newPpToken;
|
TPpToken newPpToken;
|
||||||
oldToken = ReadToken(existing->body, &oldPpToken);
|
oldToken = existing->body.getToken(parseContext, &oldPpToken);
|
||||||
newToken = ReadToken(mac.body, &newPpToken);
|
newToken = mac.body.getToken(parseContext, &newPpToken);
|
||||||
if (oldToken != newToken || oldPpToken != newPpToken) {
|
if (oldToken != newToken || oldPpToken != newPpToken) {
|
||||||
parseContext.ppError(defineLoc, "Macro redefined; different substitutions:", "#define", atomStrings.getString(defAtom));
|
parseContext.ppError(defineLoc, "Macro redefined; different substitutions:", "#define", atomStrings.getString(defAtom));
|
||||||
break;
|
break;
|
||||||
@@ -979,28 +979,16 @@ int TPpContext::scanHeaderName(TPpToken* ppToken, char delimit)
|
|||||||
// Returns nullptr if no expanded argument is created.
|
// Returns nullptr if no expanded argument is created.
|
||||||
TPpContext::TokenStream* TPpContext::PrescanMacroArg(TokenStream& arg, TPpToken* ppToken, bool newLineOkay)
|
TPpContext::TokenStream* TPpContext::PrescanMacroArg(TokenStream& arg, TPpToken* ppToken, bool newLineOkay)
|
||||||
{
|
{
|
||||||
// pre-check, to see if anything in the argument needs to be expanded,
|
|
||||||
// to see if we can kick out early
|
|
||||||
int token;
|
|
||||||
RewindTokenStream(arg);
|
|
||||||
do {
|
|
||||||
token = ReadToken(arg, ppToken);
|
|
||||||
if (token == PpAtomIdentifier && lookupMacroDef(atomStrings.getAtom(ppToken->name)) != nullptr)
|
|
||||||
break;
|
|
||||||
} while (token != EndOfInput);
|
|
||||||
|
|
||||||
// if nothing needs to be expanded, kick out early
|
|
||||||
if (token == EndOfInput)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// expand the argument
|
// expand the argument
|
||||||
TokenStream* expandedArg = new TokenStream;
|
TokenStream* expandedArg = new TokenStream;
|
||||||
pushInput(new tMarkerInput(this));
|
pushInput(new tMarkerInput(this));
|
||||||
pushTokenStreamInput(arg);
|
pushTokenStreamInput(arg);
|
||||||
|
int token;
|
||||||
while ((token = scanToken(ppToken)) != tMarkerInput::marker && token != EndOfInput) {
|
while ((token = scanToken(ppToken)) != tMarkerInput::marker && token != EndOfInput) {
|
||||||
|
token = tokenPaste(token, *ppToken);
|
||||||
if (token == PpAtomIdentifier && MacroExpand(ppToken, false, newLineOkay) != 0)
|
if (token == PpAtomIdentifier && MacroExpand(ppToken, false, newLineOkay) != 0)
|
||||||
continue;
|
continue;
|
||||||
RecordToken(*expandedArg, token, ppToken);
|
expandedArg->putToken(token, ppToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token == EndOfInput) {
|
if (token == EndOfInput) {
|
||||||
@@ -1023,7 +1011,7 @@ int TPpContext::tMacroInput::scan(TPpToken* ppToken)
|
|||||||
{
|
{
|
||||||
int token;
|
int token;
|
||||||
do {
|
do {
|
||||||
token = pp->ReadToken(mac->body, ppToken);
|
token = mac->body.getToken(pp->parseContext, ppToken);
|
||||||
} while (token == ' '); // handle white space in macro
|
} while (token == ' '); // handle white space in macro
|
||||||
|
|
||||||
// Hash operators basically turn off a round of macro substitution
|
// Hash operators basically turn off a round of macro substitution
|
||||||
@@ -1054,7 +1042,7 @@ int TPpContext::tMacroInput::scan(TPpToken* ppToken)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// see if are preceding a ##
|
// see if are preceding a ##
|
||||||
if (peekMacPasting()) {
|
if (mac->body.peekUntokenizedPasting()) {
|
||||||
prepaste = true;
|
prepaste = true;
|
||||||
pasting = true;
|
pasting = true;
|
||||||
}
|
}
|
||||||
@@ -1081,31 +1069,6 @@ int TPpContext::tMacroInput::scan(TPpToken* ppToken)
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if the next non-white-space token in the macro is ##
|
|
||||||
bool TPpContext::tMacroInput::peekMacPasting()
|
|
||||||
{
|
|
||||||
// don't return early, have to restore this
|
|
||||||
size_t savePos = mac->body.current;
|
|
||||||
|
|
||||||
// skip white-space
|
|
||||||
int ltoken;
|
|
||||||
do {
|
|
||||||
ltoken = pp->lReadByte(mac->body);
|
|
||||||
} while (ltoken == ' ');
|
|
||||||
|
|
||||||
// check for ##
|
|
||||||
bool pasting = false;
|
|
||||||
if (ltoken == '#') {
|
|
||||||
ltoken = pp->lReadByte(mac->body);
|
|
||||||
if (ltoken == '#')
|
|
||||||
pasting = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
mac->body.current = savePos;
|
|
||||||
|
|
||||||
return pasting;
|
|
||||||
}
|
|
||||||
|
|
||||||
// return a textual zero, for scanning a macro that was never defined
|
// return a textual zero, for scanning a macro that was never defined
|
||||||
int TPpContext::tZeroInput::scan(TPpToken* ppToken)
|
int TPpContext::tZeroInput::scan(TPpToken* ppToken)
|
||||||
{
|
{
|
||||||
@@ -1230,7 +1193,7 @@ int TPpContext::MacroExpand(TPpToken* ppToken, bool expandUndef, bool newLineOka
|
|||||||
depth++;
|
depth++;
|
||||||
if (token == ')')
|
if (token == ')')
|
||||||
depth--;
|
depth--;
|
||||||
RecordToken(*in->args[arg], token, ppToken);
|
in->args[arg]->putToken(token, ppToken);
|
||||||
tokenRecorded = true;
|
tokenRecorded = true;
|
||||||
}
|
}
|
||||||
if (token == ')') {
|
if (token == ')') {
|
||||||
@@ -1263,14 +1226,14 @@ int TPpContext::MacroExpand(TPpToken* ppToken, bool expandUndef, bool newLineOka
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We need both expanded and non-expanded forms of the argument, for whether or
|
// We need both expanded and non-expanded forms of the argument, for whether or
|
||||||
// not token pasting is in play.
|
// not token pasting will be applied later when the argument is consumed next to ##.
|
||||||
for (size_t i = 0; i < in->mac->args.size(); i++)
|
for (size_t i = 0; i < in->mac->args.size(); i++)
|
||||||
in->expandedArgs[i] = PrescanMacroArg(*in->args[i], ppToken, newLineOkay);
|
in->expandedArgs[i] = PrescanMacroArg(*in->args[i], ppToken, newLineOkay);
|
||||||
}
|
}
|
||||||
|
|
||||||
pushInput(in);
|
pushInput(in);
|
||||||
macro->busy = 1;
|
macro->busy = 1;
|
||||||
RewindTokenStream(macro->body);
|
macro->body.reset();
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,8 +220,26 @@ public:
|
|||||||
inputStack.pop_back();
|
inputStack.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TokenStream {
|
//
|
||||||
|
// From PpTokens.cpp
|
||||||
|
//
|
||||||
|
|
||||||
|
class TokenStream {
|
||||||
|
public:
|
||||||
TokenStream() : current(0) { }
|
TokenStream() : current(0) { }
|
||||||
|
|
||||||
|
void putToken(int token, TPpToken* ppToken);
|
||||||
|
int getToken(TParseContextBase&, TPpToken*);
|
||||||
|
bool atEnd() { return current >= data.size(); }
|
||||||
|
bool peekTokenizedPasting(bool lastTokenPastes);
|
||||||
|
bool peekUntokenizedPasting();
|
||||||
|
void reset() { current = 0; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void putSubtoken(int);
|
||||||
|
int getSubtoken();
|
||||||
|
void ungetSubtoken();
|
||||||
|
|
||||||
TVector<unsigned char> data;
|
TVector<unsigned char> data;
|
||||||
size_t current;
|
size_t current;
|
||||||
};
|
};
|
||||||
@@ -306,14 +324,13 @@ protected:
|
|||||||
virtual int getch() override { assert(0); return EndOfInput; }
|
virtual int getch() override { assert(0); return EndOfInput; }
|
||||||
virtual void ungetch() override { assert(0); }
|
virtual void ungetch() override { assert(0); }
|
||||||
bool peekPasting() override { return prepaste; }
|
bool peekPasting() override { return prepaste; }
|
||||||
bool endOfReplacementList() override { return mac->body.current >= mac->body.data.size(); }
|
bool endOfReplacementList() override { return mac->body.atEnd(); }
|
||||||
|
|
||||||
MacroSymbol *mac;
|
MacroSymbol *mac;
|
||||||
TVector<TokenStream*> args;
|
TVector<TokenStream*> args;
|
||||||
TVector<TokenStream*> expandedArgs;
|
TVector<TokenStream*> expandedArgs;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool peekMacPasting();
|
|
||||||
bool prepaste; // true if we are just before ##
|
bool prepaste; // true if we are just before ##
|
||||||
bool postpaste; // true if we are right after ##
|
bool postpaste; // true if we are right after ##
|
||||||
};
|
};
|
||||||
@@ -375,22 +392,16 @@ protected:
|
|||||||
//
|
//
|
||||||
// From PpTokens.cpp
|
// From PpTokens.cpp
|
||||||
//
|
//
|
||||||
void lAddByte(TokenStream&, unsigned char fVal);
|
|
||||||
int lReadByte(TokenStream&);
|
|
||||||
void lUnreadByte(TokenStream&);
|
|
||||||
void RecordToken(TokenStream&, int token, TPpToken* ppToken);
|
|
||||||
void RewindTokenStream(TokenStream&);
|
|
||||||
int ReadToken(TokenStream&, TPpToken*);
|
|
||||||
void pushTokenStreamInput(TokenStream&, bool pasting = false);
|
void pushTokenStreamInput(TokenStream&, bool pasting = false);
|
||||||
void UngetToken(int token, TPpToken*);
|
void UngetToken(int token, TPpToken*);
|
||||||
|
|
||||||
class tTokenInput : public tInput {
|
class tTokenInput : public tInput {
|
||||||
public:
|
public:
|
||||||
tTokenInput(TPpContext* pp, TokenStream* t, bool prepasting) : tInput(pp), tokens(t), lastTokenPastes(prepasting) { }
|
tTokenInput(TPpContext* pp, TokenStream* t, bool prepasting) : tInput(pp), tokens(t), lastTokenPastes(prepasting) { }
|
||||||
virtual int scan(TPpToken *) override;
|
virtual int scan(TPpToken *ppToken) override { return tokens->getToken(pp->parseContext, ppToken); }
|
||||||
virtual int getch() override { assert(0); return EndOfInput; }
|
virtual int getch() override { assert(0); return EndOfInput; }
|
||||||
virtual void ungetch() override { assert(0); }
|
virtual void ungetch() override { assert(0); }
|
||||||
virtual bool peekPasting() override;
|
virtual bool peekPasting() override { return tokens->peekTokenizedPasting(lastTokenPastes); }
|
||||||
protected:
|
protected:
|
||||||
TokenStream* tokens;
|
TokenStream* tokens;
|
||||||
bool lastTokenPastes; // true if the last token in the input is to be pasted, rather than consumed as a token
|
bool lastTokenPastes; // true if the last token in the input is to be pasted, rather than consumed as a token
|
||||||
|
|||||||
@@ -232,6 +232,8 @@ int TPpContext::tStringInput::scan(TPpToken* ppToken)
|
|||||||
switch (ch) {
|
switch (ch) {
|
||||||
default:
|
default:
|
||||||
// Single character token, including EndOfInput, '#' and '\' (escaped newlines are handled at a lower level, so this is just a '\' token)
|
// Single character token, including EndOfInput, '#' and '\' (escaped newlines are handled at a lower level, so this is just a '\' token)
|
||||||
|
if (ch > PpAtomMaxSingle)
|
||||||
|
ch = PpAtomBadToken;
|
||||||
return ch;
|
return ch;
|
||||||
|
|
||||||
case 'A': case 'B': case 'C': case 'D': case 'E':
|
case 'A': case 'B': case 'C': case 'D': case 'E':
|
||||||
|
|||||||
@@ -95,48 +95,45 @@ NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
|
|
||||||
namespace glslang {
|
namespace glslang {
|
||||||
|
|
||||||
void TPpContext::lAddByte(TokenStream& fTok, unsigned char fVal)
|
// push onto back of stream
|
||||||
|
void TPpContext::TokenStream::putSubtoken(int subtoken)
|
||||||
{
|
{
|
||||||
fTok.data.push_back(fVal);
|
assert((subtoken & ~0xff) == 0);
|
||||||
|
data.push_back(static_cast<unsigned char>(subtoken));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// get the next token in stream
|
||||||
* Get the next byte from a stream.
|
int TPpContext::TokenStream::getSubtoken()
|
||||||
*/
|
|
||||||
int TPpContext::lReadByte(TokenStream& pTok)
|
|
||||||
{
|
{
|
||||||
if (pTok.current < pTok.data.size())
|
if (current < data.size())
|
||||||
return pTok.data[pTok.current++];
|
return data[current++];
|
||||||
else
|
else
|
||||||
return EndOfInput;
|
return EndOfInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TPpContext::lUnreadByte(TokenStream& pTok)
|
// back up one position in the stream
|
||||||
|
void TPpContext::TokenStream::ungetSubtoken()
|
||||||
{
|
{
|
||||||
if (pTok.current > 0)
|
if (current > 0)
|
||||||
--pTok.current;
|
--current;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Add a complete token (including backing string) to the end of a list
|
||||||
* Add a token to the end of a list for later playback.
|
// for later playback.
|
||||||
*/
|
void TPpContext::TokenStream::putToken(int token, TPpToken* ppToken)
|
||||||
void TPpContext::RecordToken(TokenStream& pTok, int token, TPpToken* ppToken)
|
|
||||||
{
|
{
|
||||||
const char* s;
|
const char* s;
|
||||||
char* str = NULL;
|
char* str = NULL;
|
||||||
|
|
||||||
if (token > PpAtomMaxSingle)
|
putSubtoken(token);
|
||||||
lAddByte(pTok, (unsigned char)((token & 0x7f) + 0x80));
|
|
||||||
else
|
|
||||||
lAddByte(pTok, (unsigned char)(token & 0x7f));
|
|
||||||
|
|
||||||
switch (token) {
|
switch (token) {
|
||||||
case PpAtomIdentifier:
|
case PpAtomIdentifier:
|
||||||
case PpAtomConstString:
|
case PpAtomConstString:
|
||||||
s = ppToken->name;
|
s = ppToken->name;
|
||||||
while (*s)
|
while (*s)
|
||||||
lAddByte(pTok, (unsigned char) *s++);
|
putSubtoken(*s++);
|
||||||
lAddByte(pTok, 0);
|
putSubtoken(0);
|
||||||
break;
|
break;
|
||||||
case PpAtomConstInt:
|
case PpAtomConstInt:
|
||||||
case PpAtomConstUint:
|
case PpAtomConstUint:
|
||||||
@@ -149,46 +146,35 @@ void TPpContext::RecordToken(TokenStream& pTok, int token, TPpToken* ppToken)
|
|||||||
#endif
|
#endif
|
||||||
str = ppToken->name;
|
str = ppToken->name;
|
||||||
while (*str) {
|
while (*str) {
|
||||||
lAddByte(pTok, (unsigned char) *str);
|
putSubtoken(*str);
|
||||||
str++;
|
str++;
|
||||||
}
|
}
|
||||||
lAddByte(pTok, 0);
|
putSubtoken(0);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Read the next token from a token stream.
|
||||||
* Reset a token stream in preparation for reading.
|
// (Not the source stream, but a stream used to hold a tokenized macro).
|
||||||
*/
|
int TPpContext::TokenStream::getToken(TParseContextBase& parseContext, TPpToken *ppToken)
|
||||||
void TPpContext::RewindTokenStream(TokenStream& pTok)
|
|
||||||
{
|
{
|
||||||
pTok.current = 0;
|
int len;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Read the next token from a token stream (not the source stream, but stream used to hold a tokenized macro).
|
|
||||||
*/
|
|
||||||
int TPpContext::ReadToken(TokenStream& pTok, TPpToken *ppToken)
|
|
||||||
{
|
|
||||||
int ltoken, len;
|
|
||||||
int ch;
|
int ch;
|
||||||
|
|
||||||
ltoken = lReadByte(pTok);
|
int subtoken = getSubtoken();
|
||||||
ppToken->loc = parseContext.getCurrentLoc();
|
ppToken->loc = parseContext.getCurrentLoc();
|
||||||
if (ltoken > 127)
|
switch (subtoken) {
|
||||||
ltoken += 128;
|
|
||||||
switch (ltoken) {
|
|
||||||
case '#':
|
case '#':
|
||||||
// Check for ##, unless the current # is the last character
|
// Check for ##, unless the current # is the last character
|
||||||
if (pTok.current < pTok.data.size()) {
|
if (current < data.size()) {
|
||||||
if (lReadByte(pTok) == '#') {
|
if (getSubtoken() == '#') {
|
||||||
parseContext.requireProfile(ppToken->loc, ~EEsProfile, "token pasting (##)");
|
parseContext.requireProfile(ppToken->loc, ~EEsProfile, "token pasting (##)");
|
||||||
parseContext.profileRequires(ppToken->loc, ~EEsProfile, 130, 0, "token pasting (##)");
|
parseContext.profileRequires(ppToken->loc, ~EEsProfile, 130, 0, "token pasting (##)");
|
||||||
ltoken = PpAtomPaste;
|
subtoken = PpAtomPaste;
|
||||||
} else
|
} else
|
||||||
lUnreadByte(pTok);
|
ungetSubtoken();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PpAtomConstString:
|
case PpAtomConstString:
|
||||||
@@ -203,12 +189,12 @@ int TPpContext::ReadToken(TokenStream& pTok, TPpToken *ppToken)
|
|||||||
case PpAtomConstInt64:
|
case PpAtomConstInt64:
|
||||||
case PpAtomConstUint64:
|
case PpAtomConstUint64:
|
||||||
len = 0;
|
len = 0;
|
||||||
ch = lReadByte(pTok);
|
ch = getSubtoken();
|
||||||
while (ch != 0 && ch != EndOfInput) {
|
while (ch != 0 && ch != EndOfInput) {
|
||||||
if (len < MaxTokenLength) {
|
if (len < MaxTokenLength) {
|
||||||
ppToken->name[len] = (char)ch;
|
ppToken->name[len] = (char)ch;
|
||||||
len++;
|
len++;
|
||||||
ch = lReadByte(pTok);
|
ch = getSubtoken();
|
||||||
} else {
|
} else {
|
||||||
parseContext.error(ppToken->loc, "token too long", "", "");
|
parseContext.error(ppToken->loc, "token too long", "", "");
|
||||||
break;
|
break;
|
||||||
@@ -216,7 +202,7 @@ int TPpContext::ReadToken(TokenStream& pTok, TPpToken *ppToken)
|
|||||||
}
|
}
|
||||||
ppToken->name[len] = 0;
|
ppToken->name[len] = 0;
|
||||||
|
|
||||||
switch (ltoken) {
|
switch (subtoken) {
|
||||||
case PpAtomIdentifier:
|
case PpAtomIdentifier:
|
||||||
break;
|
break;
|
||||||
case PpAtomConstString:
|
case PpAtomConstString:
|
||||||
@@ -267,43 +253,80 @@ int TPpContext::ReadToken(TokenStream& pTok, TPpToken *ppToken)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ltoken;
|
return subtoken;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TPpContext::tTokenInput::scan(TPpToken* ppToken)
|
// We are pasting if
|
||||||
|
// 1. we are preceding a pasting operator within this stream
|
||||||
|
// or
|
||||||
|
// 2. the entire macro is preceding a pasting operator (lastTokenPastes)
|
||||||
|
// and we are also on the last token
|
||||||
|
bool TPpContext::TokenStream::peekTokenizedPasting(bool lastTokenPastes)
|
||||||
{
|
{
|
||||||
return pp->ReadToken(*tokens, ppToken);
|
// 1. preceding ##?
|
||||||
}
|
|
||||||
|
size_t savePos = current;
|
||||||
|
int subtoken;
|
||||||
|
// skip white space
|
||||||
|
do {
|
||||||
|
subtoken = getSubtoken();
|
||||||
|
} while (subtoken == ' ');
|
||||||
|
current = savePos;
|
||||||
|
if (subtoken == PpAtomPaste)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// 2. last token and we've been told after this there will be a ##
|
||||||
|
|
||||||
// We are pasting if the entire macro is preceding a pasting operator
|
|
||||||
// (lastTokenPastes) and we are also on the last token.
|
|
||||||
bool TPpContext::tTokenInput::peekPasting()
|
|
||||||
{
|
|
||||||
if (! lastTokenPastes)
|
if (! lastTokenPastes)
|
||||||
return false;
|
return false;
|
||||||
// Getting here means the last token will be pasted.
|
// Getting here means the last token will be pasted, after this
|
||||||
|
|
||||||
// Are we at the last non-whitespace token?
|
// Are we at the last non-whitespace token?
|
||||||
size_t savePos = tokens->current;
|
savePos = current;
|
||||||
bool moreTokens = false;
|
bool moreTokens = false;
|
||||||
do {
|
do {
|
||||||
int byte = pp->lReadByte(*tokens);
|
subtoken = getSubtoken();
|
||||||
if (byte == EndOfInput)
|
if (subtoken == EndOfInput)
|
||||||
break;
|
break;
|
||||||
if (byte != ' ') {
|
if (subtoken != ' ') {
|
||||||
moreTokens = true;
|
moreTokens = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (true);
|
} while (true);
|
||||||
tokens->current = savePos;
|
current = savePos;
|
||||||
|
|
||||||
return !moreTokens;
|
return !moreTokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See if the next non-white-space tokens are two consecutive #
|
||||||
|
bool TPpContext::TokenStream::peekUntokenizedPasting()
|
||||||
|
{
|
||||||
|
// don't return early, have to restore this
|
||||||
|
size_t savePos = current;
|
||||||
|
|
||||||
|
// skip white-space
|
||||||
|
int subtoken;
|
||||||
|
do {
|
||||||
|
subtoken = getSubtoken();
|
||||||
|
} while (subtoken == ' ');
|
||||||
|
|
||||||
|
// check for ##
|
||||||
|
bool pasting = false;
|
||||||
|
if (subtoken == '#') {
|
||||||
|
subtoken = getSubtoken();
|
||||||
|
if (subtoken == '#')
|
||||||
|
pasting = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = savePos;
|
||||||
|
|
||||||
|
return pasting;
|
||||||
|
}
|
||||||
|
|
||||||
void TPpContext::pushTokenStreamInput(TokenStream& ts, bool prepasting)
|
void TPpContext::pushTokenStreamInput(TokenStream& ts, bool prepasting)
|
||||||
{
|
{
|
||||||
pushInput(new tTokenInput(this, &ts, prepasting));
|
pushInput(new tTokenInput(this, &ts, prepasting));
|
||||||
RewindTokenStream(ts);
|
ts.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
int TPpContext::tUngotTokenInput::scan(TPpToken* ppToken)
|
int TPpContext::tUngotTokenInput::scan(TPpToken* ppToken)
|
||||||
|
|||||||
@@ -82,7 +82,11 @@ namespace glslang {
|
|||||||
|
|
||||||
// Multi-character tokens
|
// Multi-character tokens
|
||||||
enum EFixedAtoms {
|
enum EFixedAtoms {
|
||||||
PpAtomMaxSingle = 256, // single character tokens get their own char value as their token, skip them
|
// single character tokens get their own char value as their token; start here for multi-character tokens
|
||||||
|
PpAtomMaxSingle = 127,
|
||||||
|
|
||||||
|
// replace bad character tokens with this, to avoid accidental aliasing with the below
|
||||||
|
PpAtomBadToken,
|
||||||
|
|
||||||
// Operators
|
// Operators
|
||||||
|
|
||||||
|
|||||||
@@ -540,6 +540,7 @@ protected:
|
|||||||
bool linked;
|
bool linked;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
TProgram(TProgram&);
|
||||||
TProgram& operator=(TProgram&);
|
TProgram& operator=(TProgram&);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ namespace graphics
|
|||||||
love::Type Canvas::type("Canvas", &Texture::type);
|
love::Type Canvas::type("Canvas", &Texture::type);
|
||||||
int Canvas::canvasCount = 0;
|
int Canvas::canvasCount = 0;
|
||||||
|
|
||||||
Canvas::Canvas()
|
Canvas::Canvas(TextureType textype)
|
||||||
|
: Texture(textype)
|
||||||
{
|
{
|
||||||
canvasCount++;
|
canvasCount++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,15 +39,19 @@ public:
|
|||||||
|
|
||||||
struct Settings
|
struct Settings
|
||||||
{
|
{
|
||||||
|
int width = 1;
|
||||||
|
int height = 1;
|
||||||
|
int layers = 1; // depth for 3D textures
|
||||||
PixelFormat format = PIXELFORMAT_NORMAL;
|
PixelFormat format = PIXELFORMAT_NORMAL;
|
||||||
|
TextureType type = TEXTURE_2D;
|
||||||
float pixeldensity = 1.0f;
|
float pixeldensity = 1.0f;
|
||||||
int msaa = 0;
|
int msaa = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
Canvas();
|
Canvas(TextureType textype);
|
||||||
virtual ~Canvas();
|
virtual ~Canvas();
|
||||||
|
|
||||||
virtual love::image::ImageData *newImageData(love::image::Image *module, int x, int y, int w, int h) = 0;
|
virtual love::image::ImageData *newImageData(love::image::Image *module, int slice, int x, int y, int w, int h) = 0;
|
||||||
|
|
||||||
virtual int getMSAA() const = 0;
|
virtual int getMSAA() const = 0;
|
||||||
virtual int getRequestedMSAA() const = 0;
|
virtual int getRequestedMSAA() const = 0;
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ Font::Font(love::font::Rasterizer *r, const Texture::Filter &f)
|
|||||||
if (!r->hasGlyph(9)) // No tab character in the Rasterizer.
|
if (!r->hasGlyph(9)) // No tab character in the Rasterizer.
|
||||||
useSpacesAsTab = true;
|
useSpacesAsTab = true;
|
||||||
|
|
||||||
|
loadVolatile();
|
||||||
++fontCount;
|
++fontCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +114,72 @@ Font::TextureSize Font::getNextTextureSize() const
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Font::loadVolatile()
|
||||||
|
{
|
||||||
|
textureCacheID++;
|
||||||
|
createTexture();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Font::createTexture()
|
||||||
|
{
|
||||||
|
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||||
|
gfx->flushStreamDraws();
|
||||||
|
|
||||||
|
Image *image = nullptr;
|
||||||
|
TextureSize size = {textureWidth, textureHeight};
|
||||||
|
TextureSize nextsize = getNextTextureSize();
|
||||||
|
bool recreatetexture = false;
|
||||||
|
|
||||||
|
// If we have an existing texture already, we'll try replacing it with a
|
||||||
|
// larger-sized one rather than creating a second one. Having a single
|
||||||
|
// texture reduces texture switches and draw calls when rendering.
|
||||||
|
if ((nextsize.width > size.width || nextsize.height > size.height) && !images.empty())
|
||||||
|
{
|
||||||
|
recreatetexture = true;
|
||||||
|
size = nextsize;
|
||||||
|
images.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
Image::Settings settings;
|
||||||
|
image = gfx->newImage(TEXTURE_2D, pixelFormat, size.width, size.height, 1, settings);
|
||||||
|
image->setFilter(filter);
|
||||||
|
|
||||||
|
// Initialize the texture with transparent black.
|
||||||
|
size_t bpp = getPixelFormatSize(pixelFormat);
|
||||||
|
std::vector<uint8> emptydata(size.width * size.height * bpp, 0);
|
||||||
|
|
||||||
|
Rect rect = {0, 0, size.width, size.height};
|
||||||
|
image->replacePixels(emptydata.data(), emptydata.size(), rect, 0, 0, false);
|
||||||
|
|
||||||
|
images.emplace_back(image, Acquire::NORETAIN);
|
||||||
|
|
||||||
|
textureWidth = size.width;
|
||||||
|
textureHeight = size.height;
|
||||||
|
|
||||||
|
rowHeight = textureX = textureY = TEXTURE_PADDING;
|
||||||
|
|
||||||
|
// Re-add the old glyphs if we re-created the existing texture object.
|
||||||
|
if (recreatetexture)
|
||||||
|
{
|
||||||
|
textureCacheID++;
|
||||||
|
|
||||||
|
std::vector<uint32> glyphstoadd;
|
||||||
|
|
||||||
|
for (const auto &glyphpair : glyphs)
|
||||||
|
glyphstoadd.push_back(glyphpair.first);
|
||||||
|
|
||||||
|
glyphs.clear();
|
||||||
|
|
||||||
|
for (uint32 g : glyphstoadd)
|
||||||
|
addGlyph(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Font::unloadVolatile()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
love::font::GlyphData *Font::getRasterizerGlyphData(uint32 glyph)
|
love::font::GlyphData *Font::getRasterizerGlyphData(uint32 glyph)
|
||||||
{
|
{
|
||||||
// Use spaces for the tab 'glyph'.
|
// Use spaces for the tab 'glyph'.
|
||||||
@@ -178,7 +245,11 @@ const Font::Glyph &Font::addGlyph(uint32 glyph)
|
|||||||
// Don't waste space for empty glyphs.
|
// Don't waste space for empty glyphs.
|
||||||
if (w > 0 && h > 0)
|
if (w > 0 && h > 0)
|
||||||
{
|
{
|
||||||
uploadGlyphToTexture(gd, g);
|
Image *image = images.back();
|
||||||
|
g.texture = image;
|
||||||
|
|
||||||
|
Rect rect = {textureX, textureY, gd->getWidth(), gd->getHeight()};
|
||||||
|
image->replacePixels(gd->getData(), gd->getSize(), rect, 0, 0, false);
|
||||||
|
|
||||||
double tX = (double) textureX, tY = (double) textureY;
|
double tX = (double) textureX, tY = (double) textureY;
|
||||||
double tWidth = (double) textureWidth, tHeight = (double) textureHeight;
|
double tWidth = (double) textureWidth, tHeight = (double) textureHeight;
|
||||||
@@ -535,7 +606,7 @@ void Font::printv(graphics::Graphics *gfx, const Matrix4 &t, const std::vector<D
|
|||||||
req.formats[0] = vertex::CommonFormat::XYf_STus_RGBAub;
|
req.formats[0] = vertex::CommonFormat::XYf_STus_RGBAub;
|
||||||
req.indexMode = vertex::TriangleIndexMode::QUADS;
|
req.indexMode = vertex::TriangleIndexMode::QUADS;
|
||||||
req.vertexCount = cmd.vertexcount;
|
req.vertexCount = cmd.vertexcount;
|
||||||
req.textureHandle = cmd.texture;
|
req.texture = cmd.texture;
|
||||||
|
|
||||||
Graphics::StreamVertexData data = gfx->requestStreamDraw(req);
|
Graphics::StreamVertexData data = gfx->requestStreamDraw(req);
|
||||||
GlyphVertex *vertexdata = (GlyphVertex *) data.stream[0];
|
GlyphVertex *vertexdata = (GlyphVertex *) data.stream[0];
|
||||||
@@ -809,6 +880,14 @@ float Font::getLineHeight() const
|
|||||||
return lineHeight;
|
return lineHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Font::setFilter(const Texture::Filter &f)
|
||||||
|
{
|
||||||
|
for (const auto &image : images)
|
||||||
|
image->setFilter(f);
|
||||||
|
|
||||||
|
filter = f;
|
||||||
|
}
|
||||||
|
|
||||||
const Texture::Filter &Font::getFilter() const
|
const Texture::Filter &Font::getFilter() const
|
||||||
{
|
{
|
||||||
return filter;
|
return filter;
|
||||||
|
|||||||
@@ -33,8 +33,9 @@
|
|||||||
#include "common/Vector.h"
|
#include "common/Vector.h"
|
||||||
|
|
||||||
#include "font/Rasterizer.h"
|
#include "font/Rasterizer.h"
|
||||||
#include "Texture.h"
|
#include "Image.h"
|
||||||
#include "vertex.h"
|
#include "vertex.h"
|
||||||
|
#include "Volatile.h"
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
@@ -43,7 +44,7 @@ namespace graphics
|
|||||||
|
|
||||||
class Graphics;
|
class Graphics;
|
||||||
|
|
||||||
class Font : public Object
|
class Font : public Object, public Volatile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -88,7 +89,7 @@ public:
|
|||||||
// Used to determine when to change textures in the generated vertex array.
|
// Used to determine when to change textures in the generated vertex array.
|
||||||
struct DrawCommand
|
struct DrawCommand
|
||||||
{
|
{
|
||||||
ptrdiff_t texture;
|
Texture *texture;
|
||||||
int startvertex;
|
int startvertex;
|
||||||
int vertexcount;
|
int vertexcount;
|
||||||
};
|
};
|
||||||
@@ -155,7 +156,7 @@ public:
|
|||||||
**/
|
**/
|
||||||
float getLineHeight() const;
|
float getLineHeight() const;
|
||||||
|
|
||||||
virtual void setFilter(const Texture::Filter &f) = 0;
|
void setFilter(const Texture::Filter &f);
|
||||||
const Texture::Filter &getFilter() const;
|
const Texture::Filter &getFilter() const;
|
||||||
|
|
||||||
// Extra font metrics
|
// Extra font metrics
|
||||||
@@ -172,16 +173,20 @@ public:
|
|||||||
|
|
||||||
uint32 getTextureCacheID() const;
|
uint32 getTextureCacheID() const;
|
||||||
|
|
||||||
|
// Implements Volatile.
|
||||||
|
bool loadVolatile() override;
|
||||||
|
void unloadVolatile() override;
|
||||||
|
|
||||||
static bool getConstant(const char *in, AlignMode &out);
|
static bool getConstant(const char *in, AlignMode &out);
|
||||||
static bool getConstant(AlignMode in, const char *&out);
|
static bool getConstant(AlignMode in, const char *&out);
|
||||||
|
|
||||||
static int fontCount;
|
static int fontCount;
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
|
|
||||||
struct Glyph
|
struct Glyph
|
||||||
{
|
{
|
||||||
ptrdiff_t texture;
|
Texture *texture;
|
||||||
int spacing;
|
int spacing;
|
||||||
GlyphVertex vertices[4];
|
GlyphVertex vertices[4];
|
||||||
};
|
};
|
||||||
@@ -192,8 +197,7 @@ protected:
|
|||||||
int height;
|
int height;
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual void createTexture() = 0;
|
void createTexture();
|
||||||
virtual void uploadGlyphToTexture(font::GlyphData *data, Glyph &glyph) = 0;
|
|
||||||
|
|
||||||
TextureSize getNextTextureSize() const;
|
TextureSize getNextTextureSize() const;
|
||||||
love::font::GlyphData *getRasterizerGlyphData(uint32 glyph);
|
love::font::GlyphData *getRasterizerGlyphData(uint32 glyph);
|
||||||
@@ -210,6 +214,8 @@ protected:
|
|||||||
int textureWidth;
|
int textureWidth;
|
||||||
int textureHeight;
|
int textureHeight;
|
||||||
|
|
||||||
|
std::vector<StrongRef<love::graphics::Image>> images;
|
||||||
|
|
||||||
// maps glyphs to glyph texture information
|
// maps glyphs to glyph texture information
|
||||||
std::unordered_map<uint32, Glyph> glyphs;
|
std::unordered_map<uint32, Glyph> glyphs;
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
#include "Polyline.h"
|
#include "Polyline.h"
|
||||||
#include "font/Font.h"
|
#include "font/Font.h"
|
||||||
#include "window/Window.h"
|
#include "window/Window.h"
|
||||||
|
#include "Font.h"
|
||||||
|
#include "Video.h"
|
||||||
|
|
||||||
// C++
|
// C++
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -152,6 +154,16 @@ Quad *Graphics::newQuad(Quad::Viewport v, double sw, double sh)
|
|||||||
return new Quad(v, sw, sh);
|
return new Quad(v, sw, sh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Font *Graphics::newFont(love::font::Rasterizer *data, const Texture::Filter &filter)
|
||||||
|
{
|
||||||
|
return new Font(data, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
Video *Graphics::newVideo(love::video::VideoStream *stream, float pixeldensity)
|
||||||
|
{
|
||||||
|
return new Video(this, stream, pixeldensity);
|
||||||
|
}
|
||||||
|
|
||||||
bool Graphics::validateShader(bool gles, const Shader::ShaderSource &source, std::string &err)
|
bool Graphics::validateShader(bool gles, const Shader::ShaderSource &source, std::string &err)
|
||||||
{
|
{
|
||||||
return Shader::validate(this, gles, source, true, err);
|
return Shader::validate(this, gles, source, true, err);
|
||||||
@@ -179,9 +191,9 @@ int Graphics::getPixelHeight() const
|
|||||||
|
|
||||||
double Graphics::getCurrentPixelDensity() const
|
double Graphics::getCurrentPixelDensity() const
|
||||||
{
|
{
|
||||||
if (states.back().canvases.size() > 0)
|
if (states.back().renderTargets.size() > 0)
|
||||||
{
|
{
|
||||||
love::graphics::Canvas *c = states.back().canvases[0];
|
love::graphics::Canvas *c = states.back().renderTargets[0].canvas;
|
||||||
return (double) c->getPixelHeight() / (double) c->getHeight();
|
return (double) c->getPixelHeight() / (double) c->getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +252,7 @@ void Graphics::restoreState(const DisplayState &s)
|
|||||||
|
|
||||||
setFont(s.font.get());
|
setFont(s.font.get());
|
||||||
setShader(s.shader.get());
|
setShader(s.shader.get());
|
||||||
setCanvas(s.canvases);
|
setCanvas(s.renderTargets);
|
||||||
|
|
||||||
setColorMask(s.colorMask);
|
setColorMask(s.colorMask);
|
||||||
setWireframe(s.wireframe);
|
setWireframe(s.wireframe);
|
||||||
@@ -283,12 +295,14 @@ void Graphics::restoreStateChecked(const DisplayState &s)
|
|||||||
setFont(s.font.get());
|
setFont(s.font.get());
|
||||||
setShader(s.shader.get());
|
setShader(s.shader.get());
|
||||||
|
|
||||||
bool canvaseschanged = s.canvases.size() != cur.canvases.size();
|
bool canvaseschanged = s.renderTargets.size() != cur.renderTargets.size();
|
||||||
if (!canvaseschanged)
|
if (!canvaseschanged)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < s.canvases.size() && i < cur.canvases.size(); i++)
|
for (size_t i = 0; i < s.renderTargets.size() && i < cur.renderTargets.size(); i++)
|
||||||
{
|
{
|
||||||
if (s.canvases[i].get() != cur.canvases[i].get())
|
const auto &rt1 = s.renderTargets[i];
|
||||||
|
const auto &rt2 = cur.renderTargets[i];
|
||||||
|
if (rt1.canvas.get() != rt2.canvas.get() || rt1.slice != rt2.slice)
|
||||||
{
|
{
|
||||||
canvaseschanged = true;
|
canvaseschanged = true;
|
||||||
break;
|
break;
|
||||||
@@ -297,7 +311,7 @@ void Graphics::restoreStateChecked(const DisplayState &s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (canvaseschanged)
|
if (canvaseschanged)
|
||||||
setCanvas(s.canvases);
|
setCanvas(s.renderTargets);
|
||||||
|
|
||||||
if (s.colorMask != cur.colorMask)
|
if (s.colorMask != cur.colorMask)
|
||||||
setColorMask(s.colorMask);
|
setColorMask(s.colorMask);
|
||||||
@@ -386,47 +400,47 @@ love::graphics::Shader *Graphics::getShader() const
|
|||||||
return states.back().shader.get();
|
return states.back().shader.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::setCanvas(Canvas *canvas)
|
void Graphics::setCanvas(RenderTarget rt)
|
||||||
{
|
{
|
||||||
if (canvas == nullptr)
|
if (rt.canvas == nullptr)
|
||||||
return setCanvas();
|
return setCanvas();
|
||||||
|
|
||||||
std::vector<Canvas *> canvases = {canvas};
|
std::vector<RenderTarget> rts = {rt};
|
||||||
setCanvas(canvases);
|
setCanvas(rts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::setCanvas(const std::vector<StrongRef<Canvas>> &canvases)
|
void Graphics::setCanvas(const std::vector<RenderTargetStrongRef> &rts)
|
||||||
{
|
{
|
||||||
std::vector<Canvas *> canvaslist;
|
std::vector<RenderTarget> canvaslist;
|
||||||
canvaslist.reserve(canvases.size());
|
canvaslist.reserve(rts.size());
|
||||||
|
|
||||||
for (const StrongRef<Canvas> &c : canvases)
|
for (const auto &rt : rts)
|
||||||
canvaslist.push_back(c.get());
|
canvaslist.emplace_back(rt.canvas.get(), rt.slice);
|
||||||
|
|
||||||
return setCanvas(canvaslist);
|
return setCanvas(canvaslist);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Canvas *> Graphics::getCanvas() const
|
std::vector<Graphics::RenderTarget> Graphics::getCanvas() const
|
||||||
{
|
{
|
||||||
std::vector<Canvas *> canvases;
|
std::vector<RenderTarget> rts;
|
||||||
canvases.reserve(states.back().canvases.size());
|
rts.reserve(states.back().renderTargets.size());
|
||||||
|
|
||||||
for (const StrongRef<Canvas> &c : states.back().canvases)
|
for (const auto &rt : states.back().renderTargets)
|
||||||
canvases.push_back(c.get());
|
rts.emplace_back(rt.canvas.get(), rt.slice);
|
||||||
|
|
||||||
return canvases;
|
return rts;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Graphics::isCanvasActive() const
|
bool Graphics::isCanvasActive() const
|
||||||
{
|
{
|
||||||
return !states.back().canvases.empty();
|
return !states.back().renderTargets.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Graphics::isCanvasActive(love::graphics::Canvas *canvas) const
|
bool Graphics::isCanvasActive(love::graphics::Canvas *canvas) const
|
||||||
{
|
{
|
||||||
for (const auto &c : states.back().canvases)
|
for (const auto &rt : states.back().renderTargets)
|
||||||
{
|
{
|
||||||
if (c.get() == canvas)
|
if (rt.canvas.get() == canvas)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,7 +577,7 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawRequest &
|
|||||||
if (req.primitiveMode != state.primitiveMode
|
if (req.primitiveMode != state.primitiveMode
|
||||||
|| req.formats[0] != state.formats[0] || req.formats[1] != state.formats[1]
|
|| req.formats[0] != state.formats[0] || req.formats[1] != state.formats[1]
|
||||||
|| ((req.indexMode != TriangleIndexMode::NONE) != (state.indexCount > 0))
|
|| ((req.indexMode != TriangleIndexMode::NONE) != (state.indexCount > 0))
|
||||||
|| req.texture != state.texture || req.textureHandle != state.textureHandle)
|
|| req.texture != state.texture)
|
||||||
{
|
{
|
||||||
shouldflush = true;
|
shouldflush = true;
|
||||||
}
|
}
|
||||||
@@ -622,7 +636,6 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawRequest &
|
|||||||
state.formats[0] = req.formats[0];
|
state.formats[0] = req.formats[0];
|
||||||
state.formats[1] = req.formats[1];
|
state.formats[1] = req.formats[1];
|
||||||
state.texture = req.texture;
|
state.texture = req.texture;
|
||||||
state.textureHandle = req.textureHandle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldresize)
|
if (shouldresize)
|
||||||
@@ -1358,6 +1371,8 @@ StringMap<Graphics::Feature, Graphics::FEATURE_MAX_ENUM>::Entry Graphics::featur
|
|||||||
{ "lighten", FEATURE_LIGHTEN },
|
{ "lighten", FEATURE_LIGHTEN },
|
||||||
{ "fullnpot", FEATURE_FULL_NPOT },
|
{ "fullnpot", FEATURE_FULL_NPOT },
|
||||||
{ "pixelshaderhighp", FEATURE_PIXEL_SHADER_HIGHP },
|
{ "pixelshaderhighp", FEATURE_PIXEL_SHADER_HIGHP },
|
||||||
|
{ "arraytexture", FEATURE_ARRAY_TEXTURE },
|
||||||
|
{ "volumetexture", FEATURE_VOLUME_TEXTURE },
|
||||||
{ "glsl3", FEATURE_GLSL3 },
|
{ "glsl3", FEATURE_GLSL3 },
|
||||||
{ "instancing", FEATURE_INSTANCING },
|
{ "instancing", FEATURE_INSTANCING },
|
||||||
};
|
};
|
||||||
@@ -1368,6 +1383,9 @@ StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM>::Entry Graphics::syst
|
|||||||
{
|
{
|
||||||
{ "pointsize", LIMIT_POINT_SIZE },
|
{ "pointsize", LIMIT_POINT_SIZE },
|
||||||
{ "texturesize", LIMIT_TEXTURE_SIZE },
|
{ "texturesize", LIMIT_TEXTURE_SIZE },
|
||||||
|
{ "texturelayers", LIMIT_TEXTURE_LAYERS },
|
||||||
|
{ "volumetexturesize", LIMIT_VOLUME_TEXTURE_SIZE },
|
||||||
|
{ "cubetexturesize", LIMIT_CUBE_TEXTURE_SIZE },
|
||||||
{ "multicanvas", LIMIT_MULTI_CANVAS },
|
{ "multicanvas", LIMIT_MULTI_CANVAS },
|
||||||
{ "canvasmsaa", LIMIT_CANVAS_MSAA },
|
{ "canvasmsaa", LIMIT_CANVAS_MSAA },
|
||||||
{ "anisotropy", LIMIT_ANISOTROPY },
|
{ "anisotropy", LIMIT_ANISOTROPY },
|
||||||
|
|||||||
@@ -177,6 +177,8 @@ public:
|
|||||||
FEATURE_LIGHTEN,
|
FEATURE_LIGHTEN,
|
||||||
FEATURE_FULL_NPOT,
|
FEATURE_FULL_NPOT,
|
||||||
FEATURE_PIXEL_SHADER_HIGHP,
|
FEATURE_PIXEL_SHADER_HIGHP,
|
||||||
|
FEATURE_ARRAY_TEXTURE,
|
||||||
|
FEATURE_VOLUME_TEXTURE,
|
||||||
FEATURE_GLSL3,
|
FEATURE_GLSL3,
|
||||||
FEATURE_INSTANCING,
|
FEATURE_INSTANCING,
|
||||||
FEATURE_MAX_ENUM
|
FEATURE_MAX_ENUM
|
||||||
@@ -193,6 +195,9 @@ public:
|
|||||||
{
|
{
|
||||||
LIMIT_POINT_SIZE,
|
LIMIT_POINT_SIZE,
|
||||||
LIMIT_TEXTURE_SIZE,
|
LIMIT_TEXTURE_SIZE,
|
||||||
|
LIMIT_VOLUME_TEXTURE_SIZE,
|
||||||
|
LIMIT_CUBE_TEXTURE_SIZE,
|
||||||
|
LIMIT_TEXTURE_LAYERS,
|
||||||
LIMIT_MULTI_CANVAS,
|
LIMIT_MULTI_CANVAS,
|
||||||
LIMIT_CANVAS_MSAA,
|
LIMIT_CANVAS_MSAA,
|
||||||
LIMIT_ANISOTROPY,
|
LIMIT_ANISOTROPY,
|
||||||
@@ -262,10 +267,6 @@ public:
|
|||||||
int vertexCount = 0;
|
int vertexCount = 0;
|
||||||
Texture *texture = nullptr;
|
Texture *texture = nullptr;
|
||||||
|
|
||||||
// FIXME: This is only needed for fonts. We should just change fonts to
|
|
||||||
// use love.graphics Images instead of raw OpenGL textures.
|
|
||||||
ptrdiff_t textureHandle = 0;
|
|
||||||
|
|
||||||
StreamDrawRequest()
|
StreamDrawRequest()
|
||||||
{
|
{
|
||||||
// VS2013 can't initialize arrays in the above manner...
|
// VS2013 can't initialize arrays in the above manner...
|
||||||
@@ -312,29 +313,48 @@ public:
|
|||||||
Reference *ref;
|
Reference *ref;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct RenderTarget
|
||||||
|
{
|
||||||
|
Canvas *canvas = nullptr;
|
||||||
|
int slice = 0;
|
||||||
|
|
||||||
|
RenderTarget(Canvas *canvas, int slice = 0)
|
||||||
|
: canvas(canvas)
|
||||||
|
, slice(slice)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RenderTargetStrongRef
|
||||||
|
{
|
||||||
|
StrongRef<Canvas> canvas;
|
||||||
|
int slice = 0;
|
||||||
|
|
||||||
|
RenderTargetStrongRef(Canvas *canvas, int slice = 0)
|
||||||
|
: canvas(canvas)
|
||||||
|
, slice(slice)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
Graphics();
|
Graphics();
|
||||||
virtual ~Graphics();
|
virtual ~Graphics();
|
||||||
|
|
||||||
// Implements Module.
|
// Implements Module.
|
||||||
virtual ModuleType getModuleType() const { return M_GRAPHICS; }
|
virtual ModuleType getModuleType() const { return M_GRAPHICS; }
|
||||||
|
|
||||||
virtual Image *newImage(const std::vector<love::image::ImageData *> &data, const Image::Settings &settings) = 0;
|
virtual Image *newImage(const Image::Slices &data, const Image::Settings &settings) = 0;
|
||||||
virtual Image *newImage(const std::vector<love::image::CompressedImageData *> &cdata, const Image::Settings &settings) = 0;
|
virtual Image *newImage(TextureType textype, PixelFormat format, int width, int height, int slices, const Image::Settings &settings) = 0;
|
||||||
|
|
||||||
Quad *newQuad(Quad::Viewport v, double sw, double sh);
|
Quad *newQuad(Quad::Viewport v, double sw, double sh);
|
||||||
|
Font *newFont(love::font::Rasterizer *data, const Texture::Filter &filter = Texture::defaultFilter);
|
||||||
|
Video *newVideo(love::video::VideoStream *stream, float pixeldensity);
|
||||||
|
|
||||||
virtual SpriteBatch *newSpriteBatch(Texture *texture, int size, vertex::Usage usage) = 0;
|
virtual SpriteBatch *newSpriteBatch(Texture *texture, int size, vertex::Usage usage) = 0;
|
||||||
|
|
||||||
virtual ParticleSystem *newParticleSystem(Texture *texture, int size) = 0;
|
virtual ParticleSystem *newParticleSystem(Texture *texture, int size) = 0;
|
||||||
|
|
||||||
virtual Font *newFont(love::font::Rasterizer *data, const Texture::Filter &filter = Texture::defaultFilter) = 0;
|
virtual Canvas *newCanvas(const Canvas::Settings &settings) = 0;
|
||||||
|
|
||||||
virtual Canvas *newCanvas(int width, int height, const Canvas::Settings &settings) = 0;
|
|
||||||
|
|
||||||
virtual Shader *newShader(const Shader::ShaderSource &source) = 0;
|
virtual Shader *newShader(const Shader::ShaderSource &source) = 0;
|
||||||
|
|
||||||
virtual Video *newVideo(love::video::VideoStream *stream, float pixeldensity) = 0;
|
|
||||||
|
|
||||||
virtual Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) = 0;
|
virtual Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) = 0;
|
||||||
|
|
||||||
virtual Mesh *newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode drawmode, vertex::Usage usage) = 0;
|
virtual Mesh *newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode drawmode, vertex::Usage usage) = 0;
|
||||||
@@ -435,12 +455,12 @@ public:
|
|||||||
|
|
||||||
Shader *getShader() const;
|
Shader *getShader() const;
|
||||||
|
|
||||||
void setCanvas(Canvas *canvas);
|
void setCanvas(RenderTarget rt);
|
||||||
virtual void setCanvas(const std::vector<Canvas *> &canvases) = 0;
|
virtual void setCanvas(const std::vector<RenderTarget> &rts) = 0;
|
||||||
void setCanvas(const std::vector<StrongRef<Canvas>> &canvases);
|
void setCanvas(const std::vector<RenderTargetStrongRef> &rts);
|
||||||
virtual void setCanvas() = 0;
|
virtual void setCanvas() = 0;
|
||||||
|
|
||||||
std::vector<Canvas *> getCanvas() const;
|
std::vector<RenderTarget> getCanvas() const;
|
||||||
bool isCanvasActive() const;
|
bool isCanvasActive() const;
|
||||||
bool isCanvasActive(Canvas *canvas) const;
|
bool isCanvasActive(Canvas *canvas) const;
|
||||||
|
|
||||||
@@ -790,7 +810,7 @@ protected:
|
|||||||
StrongRef<Font> font;
|
StrongRef<Font> font;
|
||||||
StrongRef<Shader> shader;
|
StrongRef<Shader> shader;
|
||||||
|
|
||||||
std::vector<StrongRef<Canvas>> canvases;
|
std::vector<RenderTargetStrongRef> renderTargets;
|
||||||
|
|
||||||
ColorMask colorMask = ColorMask(true, true, true, true);
|
ColorMask colorMask = ColorMask(true, true, true, true);
|
||||||
|
|
||||||
@@ -810,7 +830,6 @@ protected:
|
|||||||
vertex::PrimitiveMode primitiveMode = vertex::PrimitiveMode::TRIANGLES;
|
vertex::PrimitiveMode primitiveMode = vertex::PrimitiveMode::TRIANGLES;
|
||||||
vertex::CommonFormat formats[2];
|
vertex::CommonFormat formats[2];
|
||||||
StrongRef<Texture> texture;
|
StrongRef<Texture> texture;
|
||||||
ptrdiff_t textureHandle = 0;
|
|
||||||
int vertexCount = 0;
|
int vertexCount = 0;
|
||||||
int indexCount = 0;
|
int indexCount = 0;
|
||||||
|
|
||||||
|
|||||||
+162
-14
@@ -19,6 +19,10 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
#include "Image.h"
|
#include "Image.h"
|
||||||
|
#include "Graphics.h"
|
||||||
|
|
||||||
|
// C++
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
@@ -29,9 +33,16 @@ love::Type Image::type("Image", &Texture::type);
|
|||||||
|
|
||||||
int Image::imageCount = 0;
|
int Image::imageCount = 0;
|
||||||
|
|
||||||
Image::Image(const Settings &settings)
|
Image::Image(const Slices &data, const Settings &settings, bool validatedata)
|
||||||
: settings(settings)
|
: Texture(data.getTextureType())
|
||||||
|
, settings(settings)
|
||||||
|
, data(data)
|
||||||
|
, mipmapsType(settings.mipmaps ? MIPMAPS_GENERATED : MIPMAPS_NONE)
|
||||||
|
, sRGB(isGammaCorrect() && !settings.linear)
|
||||||
{
|
{
|
||||||
|
if (validatedata && data.validate() == MIPMAPS_DATA)
|
||||||
|
mipmapsType = MIPMAPS_DATA;
|
||||||
|
|
||||||
++imageCount;
|
++imageCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,29 +51,166 @@ Image::~Image()
|
|||||||
--imageCount;
|
--imageCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Image::Settings &Image::getFlags() const
|
Image::Slices::Slices(TextureType textype)
|
||||||
|
: textureType(textype)
|
||||||
{
|
{
|
||||||
return settings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Image::getConstant(const char *in, SettingType &out)
|
void Image::Slices::clear()
|
||||||
{
|
{
|
||||||
return settingTypes.find(in, out);
|
data.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Image::getConstant(SettingType in, const char *&out)
|
void Image::Slices::set(int slice, int mipmap, love::image::ImageDataBase *d)
|
||||||
{
|
{
|
||||||
return settingTypes.find(in, out);
|
if (textureType == TEXTURE_VOLUME)
|
||||||
|
{
|
||||||
|
if (mipmap >= (int) data.size())
|
||||||
|
data.resize(mipmap + 1);
|
||||||
|
|
||||||
|
if (slice >= (int) data[mipmap].size())
|
||||||
|
data[mipmap].resize(slice + 1);
|
||||||
|
|
||||||
|
data[mipmap][slice].set(d);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (slice >= (int) data.size())
|
||||||
|
data.resize(slice + 1);
|
||||||
|
|
||||||
|
if (mipmap >= (int) data[slice].size())
|
||||||
|
data[slice].resize(mipmap + 1);
|
||||||
|
|
||||||
|
data[slice][mipmap].set(d);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StringMap<Image::SettingType, Image::SETTING_MAX_ENUM>::Entry Image::settingTypeEntries[] =
|
love::image::ImageDataBase *Image::Slices::get(int slice, int mipmap) const
|
||||||
{
|
{
|
||||||
{ "mipmaps", SETTING_MIPMAPS },
|
if (slice < 0 || slice >= getSliceCount(mipmap))
|
||||||
{ "linear", SETTING_LINEAR },
|
return nullptr;
|
||||||
{ "pixeldensity", SETTING_PIXELDENSITY },
|
|
||||||
};
|
|
||||||
|
|
||||||
StringMap<Image::SettingType, Image::SETTING_MAX_ENUM> Image::settingTypes(Image::settingTypeEntries, sizeof(Image::settingTypeEntries));
|
if (mipmap < 0 || mipmap >= getMipmapCount(slice))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (textureType == TEXTURE_VOLUME)
|
||||||
|
return data[mipmap][slice].get();
|
||||||
|
else
|
||||||
|
return data[slice][mipmap].get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::Slices::add(love::image::CompressedImageData *cdata, int startslice, int startmip, bool addallslices, bool addallmips)
|
||||||
|
{
|
||||||
|
int slicecount = addallslices ? cdata->getSliceCount() : 1;
|
||||||
|
int mipcount = addallmips ? cdata->getMipmapCount() : 1;
|
||||||
|
|
||||||
|
for (int mip = 0; mip < mipcount; mip++)
|
||||||
|
{
|
||||||
|
for (int slice = 0; slice < slicecount; slice++)
|
||||||
|
set(startslice + slice, startmip + mip, cdata->getSlice(slice, mip));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Image::Slices::getSliceCount(int mip) const
|
||||||
|
{
|
||||||
|
if (textureType == TEXTURE_VOLUME)
|
||||||
|
{
|
||||||
|
if (mip < 0 || mip >= (int) data.size())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return (int) data[mip].size();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return (int) data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Image::Slices::getMipmapCount(int slice) const
|
||||||
|
{
|
||||||
|
if (textureType == TEXTURE_VOLUME)
|
||||||
|
return (int) data.size();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (slice < 0 || slice >= (int) data.size())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return data[slice].size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image::MipmapsType Image::Slices::validate() const
|
||||||
|
{
|
||||||
|
int slicecount = getSliceCount();
|
||||||
|
int mipcount = getMipmapCount(0);
|
||||||
|
|
||||||
|
if (slicecount == 0 || mipcount == 0)
|
||||||
|
throw love::Exception("At least one ImageData or CompressedImageData is required!");
|
||||||
|
|
||||||
|
if (textureType == TEXTURE_CUBE && slicecount != 6)
|
||||||
|
throw love::Exception("Cube textures must have exactly 6 sides.");
|
||||||
|
|
||||||
|
image::ImageDataBase *firstdata = get(0, 0);
|
||||||
|
|
||||||
|
int w = firstdata->getWidth();
|
||||||
|
int h = firstdata->getHeight();
|
||||||
|
PixelFormat format = firstdata->getFormat();
|
||||||
|
|
||||||
|
int expectedmips = Texture::getMipmapCount(w, h);
|
||||||
|
|
||||||
|
if (mipcount != expectedmips && mipcount != 1)
|
||||||
|
throw love::Exception("Image does not have all required mipmap levels (expected %d, got %d)", expectedmips, mipcount);
|
||||||
|
|
||||||
|
if (textureType == TEXTURE_CUBE && w != h)
|
||||||
|
throw love::Exception("Cube images must have equal widths and heights for each cube face.");
|
||||||
|
|
||||||
|
int mipw = w;
|
||||||
|
int miph = h;
|
||||||
|
int mipslices = slicecount;
|
||||||
|
|
||||||
|
for (int mip = 0; mip < mipcount; mip++)
|
||||||
|
{
|
||||||
|
if (textureType == TEXTURE_VOLUME)
|
||||||
|
{
|
||||||
|
slicecount = getSliceCount(mip);
|
||||||
|
|
||||||
|
if (slicecount != mipslices)
|
||||||
|
throw love::Exception("Invalid number of image data layers in mipmap level %d (expected %d, got %d)", mip+1, mipslices, slicecount);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int slice = 0; slice < slicecount; slice++)
|
||||||
|
{
|
||||||
|
auto slicedata = get(slice, mip);
|
||||||
|
|
||||||
|
if (slicedata == nullptr)
|
||||||
|
throw love::Exception("Missing image data (slice %d, mipmap level %d)", slice+1, mip+1);
|
||||||
|
|
||||||
|
int realw = slicedata->getWidth();
|
||||||
|
int realh = slicedata->getHeight();
|
||||||
|
|
||||||
|
if (getMipmapCount(slice) != mipcount)
|
||||||
|
throw love::Exception("All Image layers must have the same mipmap count.");
|
||||||
|
|
||||||
|
if (mipw != realw)
|
||||||
|
throw love::Exception("Width of image data (slice %d, mipmap level %d) is incorrect (expected %d, got %d)", slice+1, mip+1, mipw, realw);
|
||||||
|
|
||||||
|
if (miph != realh)
|
||||||
|
throw love::Exception("Height of image data (slice %d, mipmap level %d) is incorrect (expected %d, got %d)", slice+1, mip+1, miph, realh);
|
||||||
|
|
||||||
|
if (format != slicedata->getFormat())
|
||||||
|
throw love::Exception("All Image slices and mipmaps must have the same pixel format.");
|
||||||
|
}
|
||||||
|
|
||||||
|
mipw = std::max(mipw / 2, 1);
|
||||||
|
miph = std::max(miph / 2, 1);
|
||||||
|
|
||||||
|
if (textureType == TEXTURE_VOLUME)
|
||||||
|
mipslices = std::max(mipslices / 2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mipcount > 1)
|
||||||
|
return MIPMAPS_DATA;
|
||||||
|
else
|
||||||
|
return MIPMAPS_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
} // graphics
|
} // graphics
|
||||||
} // love
|
} // love
|
||||||
|
|||||||
@@ -39,12 +39,11 @@ public:
|
|||||||
|
|
||||||
static love::Type type;
|
static love::Type type;
|
||||||
|
|
||||||
enum SettingType
|
enum MipmapsType
|
||||||
{
|
{
|
||||||
SETTING_MIPMAPS,
|
MIPMAPS_NONE,
|
||||||
SETTING_LINEAR,
|
MIPMAPS_DATA,
|
||||||
SETTING_PIXELDENSITY,
|
MIPMAPS_GENERATED,
|
||||||
SETTING_MAX_ENUM
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Settings
|
struct Settings
|
||||||
@@ -54,33 +53,59 @@ public:
|
|||||||
float pixeldensity = 1.0f;
|
float pixeldensity = 1.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
Image(const Settings &settings);
|
struct Slices
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
Slices(TextureType textype);
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
void set(int slice, int mipmap, love::image::ImageDataBase *data);
|
||||||
|
love::image::ImageDataBase *get(int slice, int mipmap) const;
|
||||||
|
|
||||||
|
void add(love::image::CompressedImageData *cdata, int startslice, int startmip, bool addallslices, bool addallmips);
|
||||||
|
|
||||||
|
int getSliceCount(int mip = 0) const;
|
||||||
|
int getMipmapCount(int slice = 0) const;
|
||||||
|
|
||||||
|
MipmapsType validate() const;
|
||||||
|
|
||||||
|
TextureType getTextureType() const { return textureType; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
TextureType textureType;
|
||||||
|
|
||||||
|
// For 2D/Cube/2DArray texture types, each element in the data array has
|
||||||
|
// an array of mipmap levels. For 3D texture types, each mipmap level
|
||||||
|
// has an array of layers.
|
||||||
|
std::vector<std::vector<StrongRef<love::image::ImageDataBase>>> data;
|
||||||
|
|
||||||
|
}; // Slices
|
||||||
|
|
||||||
virtual ~Image();
|
virtual ~Image();
|
||||||
|
|
||||||
virtual const std::vector<StrongRef<love::image::ImageData>> &getImageData() const = 0;
|
virtual void replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, bool reloadmipmaps) = 0;
|
||||||
virtual const std::vector<StrongRef<love::image::CompressedImageData>> &getCompressedData() const = 0;
|
virtual void replacePixels(const void *data, size_t size, const Rect &rect, int slice, int mipmap, bool reloadmipmaps) = 0;
|
||||||
|
|
||||||
virtual void setMipmapSharpness(float sharpness) = 0;
|
|
||||||
virtual float getMipmapSharpness() const = 0;
|
|
||||||
|
|
||||||
|
virtual bool isFormatLinear() const = 0;
|
||||||
virtual bool isCompressed() const = 0;
|
virtual bool isCompressed() const = 0;
|
||||||
|
|
||||||
virtual bool refresh(int xoffset, int yoffset, int w, int h) = 0;
|
virtual MipmapsType getMipmapsType() const = 0;
|
||||||
|
|
||||||
const Settings &getFlags() const;
|
|
||||||
|
|
||||||
static bool getConstant(const char *in, SettingType &out);
|
|
||||||
static bool getConstant(SettingType in, const char *&out);
|
|
||||||
|
|
||||||
static int imageCount;
|
static int imageCount;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
Image(const Slices &data, const Settings &settings, bool validatedata);
|
||||||
|
|
||||||
// The settings used to initialize this Image.
|
// The settings used to initialize this Image.
|
||||||
Settings settings;
|
Settings settings;
|
||||||
|
|
||||||
static StringMap<SettingType, SETTING_MAX_ENUM>::Entry settingTypeEntries[];
|
Slices data;
|
||||||
static StringMap<SettingType, SETTING_MAX_ENUM> settingTypes;
|
|
||||||
|
MipmapsType mipmapsType;
|
||||||
|
bool sRGB;
|
||||||
|
|
||||||
}; // Image
|
}; // Image
|
||||||
|
|
||||||
|
|||||||
@@ -174,6 +174,14 @@ void Shader::attachDefault()
|
|||||||
current = nullptr;
|
current = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Shader::checkMainTextureType(TextureType textype) const
|
||||||
|
{
|
||||||
|
const UniformInfo *info = getUniformInfo(BUILTIN_TEXTURE_MAIN);
|
||||||
|
|
||||||
|
if (info != nullptr && info->textureType != TEXTURE_MAX_ENUM && info->textureType != textype)
|
||||||
|
throw love::Exception("Texture's type must match the type of the shader's main texture.");
|
||||||
|
}
|
||||||
|
|
||||||
bool Shader::validate(Graphics *gfx, bool gles, const ShaderSource &source, bool checkWithDefaults, std::string &err)
|
bool Shader::validate(Graphics *gfx, bool gles, const ShaderSource &source, bool checkWithDefaults, std::string &err)
|
||||||
{
|
{
|
||||||
if (source.vertex.empty() && source.pixel.empty())
|
if (source.vertex.empty() && source.pixel.empty())
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
UniformType baseType;
|
UniformType baseType;
|
||||||
|
TextureType textureType;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
union
|
union
|
||||||
@@ -144,11 +145,8 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds this Shader's program to be used when rendering.
|
* Binds this Shader's program to be used when rendering.
|
||||||
*
|
|
||||||
* @param temporary True if we just want to send values to the shader with
|
|
||||||
* no intention of rendering.
|
|
||||||
**/
|
**/
|
||||||
virtual void attach(bool temporary = false) = 0;
|
virtual void attach() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attach the default shader.
|
* Attach the default shader.
|
||||||
@@ -161,9 +159,11 @@ public:
|
|||||||
virtual std::string getWarnings() const = 0;
|
virtual std::string getWarnings() const = 0;
|
||||||
|
|
||||||
virtual const UniformInfo *getUniformInfo(const std::string &name) const = 0;
|
virtual const UniformInfo *getUniformInfo(const std::string &name) const = 0;
|
||||||
virtual void updateUniform(const UniformInfo *info, int count, bool internalUpdate = false) = 0;
|
virtual const UniformInfo *getUniformInfo(BuiltinUniform builtin) const = 0;
|
||||||
|
|
||||||
virtual void sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate = false) = 0;
|
virtual void updateUniform(const UniformInfo *info, int count) = 0;
|
||||||
|
|
||||||
|
virtual void sendTextures(const UniformInfo *info, Texture **textures, int count) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets whether a uniform with the specified name exists and is actively
|
* Gets whether a uniform with the specified name exists and is actively
|
||||||
@@ -174,7 +174,9 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Sets the textures used when rendering a video. For internal use only.
|
* Sets the textures used when rendering a video. For internal use only.
|
||||||
**/
|
**/
|
||||||
virtual void setVideoTextures(ptrdiff_t ytexture, ptrdiff_t cbtexture, ptrdiff_t crtexture) = 0;
|
virtual void setVideoTextures(Texture *ytexture, Texture *cbtexture, Texture *crtexture) = 0;
|
||||||
|
|
||||||
|
void checkMainTextureType(TextureType textype) const;
|
||||||
|
|
||||||
virtual ptrdiff_t getHandle() const = 0;
|
virtual ptrdiff_t getHandle() const = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,25 @@
|
|||||||
* 3. This notice may not be removed or altered from any source distribution.
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
// LOVE
|
||||||
|
#include "common/config.h"
|
||||||
#include "Texture.h"
|
#include "Texture.h"
|
||||||
#include "Graphics.h"
|
#include "Graphics.h"
|
||||||
|
|
||||||
|
// C
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#ifdef LOVE_ANDROID
|
||||||
|
// log2 is not declared in the math.h shipped with the Android NDK
|
||||||
|
static inline double log2(double n)
|
||||||
|
{
|
||||||
|
// log(n)/log(2) is log2.
|
||||||
|
return std::log(n) / std::log(2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
namespace graphics
|
namespace graphics
|
||||||
@@ -32,14 +48,19 @@ Texture::Filter Texture::defaultFilter;
|
|||||||
Texture::FilterMode Texture::defaultMipmapFilter = Texture::FILTER_LINEAR;
|
Texture::FilterMode Texture::defaultMipmapFilter = Texture::FILTER_LINEAR;
|
||||||
float Texture::defaultMipmapSharpness = 0.0f;
|
float Texture::defaultMipmapSharpness = 0.0f;
|
||||||
|
|
||||||
Texture::Texture()
|
Texture::Texture(TextureType texType)
|
||||||
: format(PIXELFORMAT_UNKNOWN)
|
: texType(texType)
|
||||||
|
, format(PIXELFORMAT_UNKNOWN)
|
||||||
, width(0)
|
, width(0)
|
||||||
, height(0)
|
, height(0)
|
||||||
|
, depth(1)
|
||||||
|
, layers(1)
|
||||||
|
, mipmapCount(1)
|
||||||
, pixelWidth(0)
|
, pixelWidth(0)
|
||||||
, pixelHeight(0)
|
, pixelHeight(0)
|
||||||
, filter(defaultFilter)
|
, filter(defaultFilter)
|
||||||
, wrap()
|
, wrap()
|
||||||
|
, mipmapSharpness(defaultMipmapSharpness)
|
||||||
, vertices()
|
, vertices()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -48,6 +69,39 @@ Texture::~Texture()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Texture::initVertices()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
vertices[i].color = Color(255, 255, 255, 255);
|
||||||
|
|
||||||
|
// Vertices are ordered for use with triangle strips:
|
||||||
|
// 0---2
|
||||||
|
// | / |
|
||||||
|
// 1---3
|
||||||
|
vertices[0].x = 0.0f;
|
||||||
|
vertices[0].y = 0.0f;
|
||||||
|
vertices[1].x = 0.0f;
|
||||||
|
vertices[1].y = (float) height;
|
||||||
|
vertices[2].x = (float) width;
|
||||||
|
vertices[2].y = 0.0f;
|
||||||
|
vertices[3].x = (float) width;
|
||||||
|
vertices[3].y = (float) height;
|
||||||
|
|
||||||
|
vertices[0].s = 0.0f;
|
||||||
|
vertices[0].t = 0.0f;
|
||||||
|
vertices[1].s = 0.0f;
|
||||||
|
vertices[1].t = 1.0f;
|
||||||
|
vertices[2].s = 1.0f;
|
||||||
|
vertices[2].t = 0.0f;
|
||||||
|
vertices[3].s = 1.0f;
|
||||||
|
vertices[3].t = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureType Texture::getTextureType() const
|
||||||
|
{
|
||||||
|
return texType;
|
||||||
|
}
|
||||||
|
|
||||||
PixelFormat Texture::getPixelFormat() const
|
PixelFormat Texture::getPixelFormat() const
|
||||||
{
|
{
|
||||||
return format;
|
return format;
|
||||||
@@ -65,6 +119,9 @@ void Texture::drawq(Graphics *gfx, Quad *quad, const Matrix4 &m)
|
|||||||
|
|
||||||
void Texture::drawv(Graphics *gfx, const Matrix4 &localTransform, const Vertex *v)
|
void Texture::drawv(Graphics *gfx, const Matrix4 &localTransform, const Vertex *v)
|
||||||
{
|
{
|
||||||
|
if (Shader::current)
|
||||||
|
Shader::current->checkMainTextureType(texType);
|
||||||
|
|
||||||
Matrix4 t(gfx->getTransform(), localTransform);
|
Matrix4 t(gfx->getTransform(), localTransform);
|
||||||
|
|
||||||
Vertex verts[4] = {v[0], v[1], v[2], v[3]};
|
Vertex verts[4] = {v[0], v[1], v[2], v[3]};
|
||||||
@@ -95,6 +152,21 @@ int Texture::getHeight() const
|
|||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Texture::getDepth() const
|
||||||
|
{
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Texture::getLayerCount() const
|
||||||
|
{
|
||||||
|
return layers;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Texture::getMipmapCount() const
|
||||||
|
{
|
||||||
|
return mipmapCount;
|
||||||
|
}
|
||||||
|
|
||||||
int Texture::getPixelWidth() const
|
int Texture::getPixelWidth() const
|
||||||
{
|
{
|
||||||
return pixelWidth;
|
return pixelWidth;
|
||||||
@@ -120,6 +192,11 @@ const Texture::Wrap &Texture::getWrap() const
|
|||||||
return wrap;
|
return wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float Texture::getMipmapSharpness() const
|
||||||
|
{
|
||||||
|
return mipmapSharpness;
|
||||||
|
}
|
||||||
|
|
||||||
const Vertex *Texture::getVertices() const
|
const Vertex *Texture::getVertices() const
|
||||||
{
|
{
|
||||||
return vertices;
|
return vertices;
|
||||||
@@ -142,6 +219,26 @@ bool Texture::validateFilter(const Filter &f, bool mipmapsAllowed)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Texture::getMipmapCount(int w, int h)
|
||||||
|
{
|
||||||
|
return (int) log2(std::max(w, h)) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Texture::getMipmapCount(int w, int h, int d)
|
||||||
|
{
|
||||||
|
return (int) log2(std::max(std::max(w, h), d)) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Texture::getConstant(const char *in, TextureType &out)
|
||||||
|
{
|
||||||
|
return texTypes.find(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Texture::getConstant(TextureType in, const char *&out)
|
||||||
|
{
|
||||||
|
return texTypes.find(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
bool Texture::getConstant(const char *in, FilterMode &out)
|
bool Texture::getConstant(const char *in, FilterMode &out)
|
||||||
{
|
{
|
||||||
return filterModes.find(in, out);
|
return filterModes.find(in, out);
|
||||||
@@ -162,6 +259,16 @@ bool Texture::getConstant(WrapMode in, const char *&out)
|
|||||||
return wrapModes.find(in, out);
|
return wrapModes.find(in, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringMap<TextureType, TEXTURE_MAX_ENUM>::Entry Texture::texTypeEntries[] =
|
||||||
|
{
|
||||||
|
{ "2d", TEXTURE_2D },
|
||||||
|
{ "volume", TEXTURE_VOLUME },
|
||||||
|
{ "array", TEXTURE_2D_ARRAY },
|
||||||
|
{ "cube", TEXTURE_CUBE },
|
||||||
|
};
|
||||||
|
|
||||||
|
StringMap<TextureType, TEXTURE_MAX_ENUM> Texture::texTypes(Texture::texTypeEntries, sizeof(Texture::texTypeEntries));
|
||||||
|
|
||||||
StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM>::Entry Texture::filterModeEntries[] =
|
StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM>::Entry Texture::filterModeEntries[] =
|
||||||
{
|
{
|
||||||
{ "linear", FILTER_LINEAR },
|
{ "linear", FILTER_LINEAR },
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "common/StringMap.h"
|
#include "common/StringMap.h"
|
||||||
#include "common/math.h"
|
#include "common/math.h"
|
||||||
#include "common/pixelformat.h"
|
#include "common/pixelformat.h"
|
||||||
|
#include "common/Exception.h"
|
||||||
#include "Drawable.h"
|
#include "Drawable.h"
|
||||||
#include "Quad.h"
|
#include "Quad.h"
|
||||||
#include "vertex.h"
|
#include "vertex.h"
|
||||||
@@ -37,6 +38,25 @@ namespace love
|
|||||||
namespace graphics
|
namespace graphics
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class Graphics;
|
||||||
|
|
||||||
|
enum TextureType
|
||||||
|
{
|
||||||
|
TEXTURE_2D,
|
||||||
|
TEXTURE_VOLUME,
|
||||||
|
TEXTURE_2D_ARRAY,
|
||||||
|
TEXTURE_CUBE,
|
||||||
|
TEXTURE_MAX_ENUM
|
||||||
|
};
|
||||||
|
|
||||||
|
class TextureTooLargeException : public love::Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TextureTooLargeException(const char *dimname, int pix)
|
||||||
|
: Exception("Cannot create texture: %s of %d pixels is too large for this system.", dimname, pix)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for 2D textures. All textures can be drawn with Quads, have a
|
* Base class for 2D textures. All textures can be drawn with Quads, have a
|
||||||
* width and height, and have filter and wrap modes.
|
* width and height, and have filter and wrap modes.
|
||||||
@@ -76,9 +96,10 @@ public:
|
|||||||
{
|
{
|
||||||
WrapMode s = WRAP_CLAMP;
|
WrapMode s = WRAP_CLAMP;
|
||||||
WrapMode t = WRAP_CLAMP;
|
WrapMode t = WRAP_CLAMP;
|
||||||
|
WrapMode r = WRAP_CLAMP;
|
||||||
};
|
};
|
||||||
|
|
||||||
Texture();
|
Texture(TextureType texType);
|
||||||
virtual ~Texture();
|
virtual ~Texture();
|
||||||
|
|
||||||
static Filter defaultFilter;
|
static Filter defaultFilter;
|
||||||
@@ -93,10 +114,14 @@ public:
|
|||||||
**/
|
**/
|
||||||
void drawq(Graphics *gfx, Quad *quad, const Matrix4 &m);
|
void drawq(Graphics *gfx, Quad *quad, const Matrix4 &m);
|
||||||
|
|
||||||
|
TextureType getTextureType() const;
|
||||||
PixelFormat getPixelFormat() const;
|
PixelFormat getPixelFormat() const;
|
||||||
|
|
||||||
virtual int getWidth() const;
|
int getWidth() const;
|
||||||
virtual int getHeight() const;
|
int getHeight() const;
|
||||||
|
int getDepth() const;
|
||||||
|
int getLayerCount() const;
|
||||||
|
int getMipmapCount() const;
|
||||||
|
|
||||||
virtual int getPixelWidth() const;
|
virtual int getPixelWidth() const;
|
||||||
virtual int getPixelHeight() const;
|
virtual int getPixelHeight() const;
|
||||||
@@ -109,12 +134,22 @@ public:
|
|||||||
virtual bool setWrap(const Wrap &w) = 0;
|
virtual bool setWrap(const Wrap &w) = 0;
|
||||||
virtual const Wrap &getWrap() const;
|
virtual const Wrap &getWrap() const;
|
||||||
|
|
||||||
|
// Sets the mipmap texture LOD bias (sharpness) value.
|
||||||
|
virtual bool setMipmapSharpness(float sharpness) = 0;
|
||||||
|
float getMipmapSharpness() const;
|
||||||
|
|
||||||
virtual const Vertex *getVertices() const;
|
virtual const Vertex *getVertices() const;
|
||||||
|
|
||||||
virtual ptrdiff_t getHandle() const = 0;
|
virtual ptrdiff_t getHandle() const = 0;
|
||||||
|
|
||||||
static bool validateFilter(const Filter &f, bool mipmapsAllowed);
|
static bool validateFilter(const Filter &f, bool mipmapsAllowed);
|
||||||
|
|
||||||
|
static int getMipmapCount(int w, int h);
|
||||||
|
static int getMipmapCount(int w, int h, int d);
|
||||||
|
|
||||||
|
static bool getConstant(const char *in, TextureType &out);
|
||||||
|
static bool getConstant(TextureType in, const char *&out);
|
||||||
|
|
||||||
static bool getConstant(const char *in, FilterMode &out);
|
static bool getConstant(const char *in, FilterMode &out);
|
||||||
static bool getConstant(FilterMode in, const char *&out);
|
static bool getConstant(FilterMode in, const char *&out);
|
||||||
|
|
||||||
@@ -123,23 +158,35 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
void initVertices();
|
||||||
virtual void drawv(Graphics *gfx, const Matrix4 &localTransform, const Vertex *v);
|
virtual void drawv(Graphics *gfx, const Matrix4 &localTransform, const Vertex *v);
|
||||||
|
|
||||||
|
TextureType texType;
|
||||||
|
|
||||||
PixelFormat format;
|
PixelFormat format;
|
||||||
|
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
|
int depth;
|
||||||
|
int layers;
|
||||||
|
int mipmapCount;
|
||||||
|
|
||||||
int pixelWidth;
|
int pixelWidth;
|
||||||
int pixelHeight;
|
int pixelHeight;
|
||||||
|
|
||||||
|
|
||||||
Filter filter;
|
Filter filter;
|
||||||
Wrap wrap;
|
Wrap wrap;
|
||||||
|
|
||||||
|
float mipmapSharpness;
|
||||||
|
|
||||||
Vertex vertices[4];
|
Vertex vertices[4];
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
static StringMap<TextureType, TEXTURE_MAX_ENUM>::Entry texTypeEntries[];
|
||||||
|
static StringMap<TextureType, TEXTURE_MAX_ENUM> texTypes;
|
||||||
|
|
||||||
static StringMap<FilterMode, FILTER_MAX_ENUM>::Entry filterModeEntries[];
|
static StringMap<FilterMode, FILTER_MAX_ENUM>::Entry filterModeEntries[];
|
||||||
static StringMap<FilterMode, FILTER_MAX_ENUM> filterModes;
|
static StringMap<FilterMode, FILTER_MAX_ENUM> filterModes;
|
||||||
|
|
||||||
|
|||||||
@@ -31,14 +31,12 @@ namespace graphics
|
|||||||
|
|
||||||
love::Type Video::type("Video", &Drawable::type);
|
love::Type Video::type("Video", &Drawable::type);
|
||||||
|
|
||||||
Video::Video(love::video::VideoStream *stream, float pixeldensity)
|
Video::Video(Graphics *gfx, love::video::VideoStream *stream, float pixeldensity)
|
||||||
: stream(stream)
|
: stream(stream)
|
||||||
, width(stream->getWidth() / pixeldensity)
|
, width(stream->getWidth() / pixeldensity)
|
||||||
, height(stream->getHeight() / pixeldensity)
|
, height(stream->getHeight() / pixeldensity)
|
||||||
, filter(Texture::defaultFilter)
|
, filter(Texture::defaultFilter)
|
||||||
{
|
{
|
||||||
textureHandles[2] = textureHandles[1] = textureHandles[0] = 0;
|
|
||||||
|
|
||||||
filter.mipmap = Texture::FILTER_NONE;
|
filter.mipmap = Texture::FILTER_NONE;
|
||||||
|
|
||||||
stream->fillBackBuffer();
|
stream->fillBackBuffer();
|
||||||
@@ -67,6 +65,33 @@ Video::Video(love::video::VideoStream *stream, float pixeldensity)
|
|||||||
vertices[2].t = 0.0f;
|
vertices[2].t = 0.0f;
|
||||||
vertices[3].s = 1.0f;
|
vertices[3].s = 1.0f;
|
||||||
vertices[3].t = 1.0f;
|
vertices[3].t = 1.0f;
|
||||||
|
|
||||||
|
// Create the textures using the initial frame data.
|
||||||
|
auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer();
|
||||||
|
|
||||||
|
int widths[3] = {frame->yw, frame->cw, frame->cw};
|
||||||
|
int heights[3] = {frame->yh, frame->ch, frame->ch};
|
||||||
|
|
||||||
|
const unsigned char *data[3] = {frame->yplane, frame->cbplane, frame->crplane};
|
||||||
|
|
||||||
|
Texture::Wrap wrap; // Clamp wrap mode.
|
||||||
|
Image::Settings settings;
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
Image *img = gfx->newImage(TEXTURE_2D, PIXELFORMAT_R8, widths[i], heights[i], 1, settings);
|
||||||
|
|
||||||
|
img->setFilter(filter);
|
||||||
|
img->setWrap(wrap);
|
||||||
|
|
||||||
|
size_t bpp = getPixelFormatSize(PIXELFORMAT_R8);
|
||||||
|
size_t size = bpp * widths[i] * heights[i];
|
||||||
|
|
||||||
|
Rect rect = {0, 0, widths[i], heights[i]};
|
||||||
|
img->replacePixels(data[i], size, rect, 0, 0, false);
|
||||||
|
|
||||||
|
images[i].set(img, Acquire::NORETAIN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Video::~Video()
|
Video::~Video()
|
||||||
@@ -93,7 +118,7 @@ void Video::draw(Graphics *gfx, const Matrix4 &m)
|
|||||||
shader = Shader::defaultVideoShader;
|
shader = Shader::defaultVideoShader;
|
||||||
}
|
}
|
||||||
|
|
||||||
shader->setVideoTextures(textureHandles[0], textureHandles[1], textureHandles[2]);
|
shader->setVideoTextures(images[0], images[1], images[2]);
|
||||||
|
|
||||||
Graphics::StreamDrawRequest req;
|
Graphics::StreamDrawRequest req;
|
||||||
req.formats[0] = vertex::CommonFormat::XYf_STf_RGBAub;
|
req.formats[0] = vertex::CommonFormat::XYf_STf_RGBAub;
|
||||||
@@ -129,7 +154,20 @@ void Video::update()
|
|||||||
if (bufferschanged)
|
if (bufferschanged)
|
||||||
{
|
{
|
||||||
auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer();
|
auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer();
|
||||||
uploadFrame(frame);
|
|
||||||
|
int widths[3] = {frame->yw, frame->cw, frame->cw};
|
||||||
|
int heights[3] = {frame->yh, frame->ch, frame->ch};
|
||||||
|
|
||||||
|
const unsigned char *data[3] = {frame->yplane, frame->cbplane, frame->crplane};
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
size_t bpp = getPixelFormatSize(PIXELFORMAT_R8);
|
||||||
|
size_t size = bpp * widths[i] * heights[i];
|
||||||
|
|
||||||
|
Rect rect = {0, 0, widths[i], heights[i]};
|
||||||
|
images[i]->replacePixels(data[i], size, rect, 0, 0, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +201,14 @@ int Video::getPixelHeight() const
|
|||||||
return stream->getHeight();
|
return stream->getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Video::setFilter(const Texture::Filter &f)
|
||||||
|
{
|
||||||
|
for (const auto &image : images)
|
||||||
|
image->setFilter(f);
|
||||||
|
|
||||||
|
filter = f;
|
||||||
|
}
|
||||||
|
|
||||||
const Texture::Filter &Video::getFilter() const
|
const Texture::Filter &Video::getFilter() const
|
||||||
{
|
{
|
||||||
return filter;
|
return filter;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
// LOVE
|
// LOVE
|
||||||
#include "common/math.h"
|
#include "common/math.h"
|
||||||
#include "Drawable.h"
|
#include "Drawable.h"
|
||||||
#include "Texture.h"
|
#include "Image.h"
|
||||||
#include "vertex.h"
|
#include "vertex.h"
|
||||||
#include "video/VideoStream.h"
|
#include "video/VideoStream.h"
|
||||||
#include "audio/Source.h"
|
#include "audio/Source.h"
|
||||||
@@ -33,13 +33,15 @@ namespace love
|
|||||||
namespace graphics
|
namespace graphics
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class Graphics;
|
||||||
|
|
||||||
class Video : public Drawable
|
class Video : public Drawable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
static love::Type type;
|
static love::Type type;
|
||||||
|
|
||||||
Video(love::video::VideoStream *stream, float pixeldensity = 1.0f);
|
Video(Graphics *gfx, love::video::VideoStream *stream, float pixeldensity = 1.0f);
|
||||||
virtual ~Video();
|
virtual ~Video();
|
||||||
|
|
||||||
// Drawable
|
// Drawable
|
||||||
@@ -56,12 +58,12 @@ public:
|
|||||||
int getPixelWidth() const;
|
int getPixelWidth() const;
|
||||||
int getPixelHeight() const;
|
int getPixelHeight() const;
|
||||||
|
|
||||||
virtual void setFilter(const Texture::Filter &f) = 0;
|
void setFilter(const Texture::Filter &f);
|
||||||
const Texture::Filter &getFilter() const;
|
const Texture::Filter &getFilter() const;
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
|
|
||||||
virtual void uploadFrame(const love::video::VideoStream::Frame *frame) = 0;
|
void update();
|
||||||
|
|
||||||
StrongRef<love::video::VideoStream> stream;
|
StrongRef<love::video::VideoStream> stream;
|
||||||
|
|
||||||
@@ -72,11 +74,7 @@ protected:
|
|||||||
|
|
||||||
Vertex vertices[4];
|
Vertex vertices[4];
|
||||||
|
|
||||||
ptrdiff_t textureHandles[3];
|
StrongRef<Image> images[3];
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void update();
|
|
||||||
StrongRef<love::audio::Source> source;
|
StrongRef<love::audio::Source> source;
|
||||||
|
|
||||||
}; // Video
|
}; // Video
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace graphics
|
|||||||
namespace opengl
|
namespace opengl
|
||||||
{
|
{
|
||||||
|
|
||||||
static GLenum createFBO(GLuint &framebuffer, GLuint texture)
|
static GLenum createFBO(GLuint &framebuffer, TextureType texType, GLuint texture, int layers, bool initialize)
|
||||||
{
|
{
|
||||||
// get currently bound fbo to reset to it later
|
// get currently bound fbo to reset to it later
|
||||||
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
|
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
|
||||||
@@ -40,12 +40,28 @@ static GLenum createFBO(GLuint &framebuffer, GLuint texture)
|
|||||||
|
|
||||||
if (texture != 0)
|
if (texture != 0)
|
||||||
{
|
{
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
|
if (initialize)
|
||||||
|
{
|
||||||
|
int faces = texType == TEXTURE_CUBE ? 6 : 1;
|
||||||
|
|
||||||
// Initialize the texture to transparent black.
|
// Make sure all faces and layers of the texture are initialized to
|
||||||
|
// transparent black. This is unfortunately probably pretty slow for
|
||||||
|
// 2D-array and 3D textures with a lot of layers...
|
||||||
|
for (int layer = layers - 1; layer >= 0; layer--)
|
||||||
|
{
|
||||||
|
for (int face = faces - 1; face >= 0; face--)
|
||||||
|
{
|
||||||
|
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, 0, layer, face);
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, 0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||||
|
|
||||||
@@ -96,46 +112,39 @@ static bool createMSAABuffer(int width, int height, int &samples, PixelFormat pi
|
|||||||
return status == GL_FRAMEBUFFER_COMPLETE && samples > 1;
|
return status == GL_FRAMEBUFFER_COMPLETE && samples > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Canvas::Canvas(int width, int height, const Settings &settings)
|
Canvas::Canvas(const Settings &settings)
|
||||||
: settings(settings)
|
: love::graphics::Canvas(settings.type)
|
||||||
, fbo(0)
|
, fbo(0)
|
||||||
, texture(0)
|
, texture(0)
|
||||||
, msaa_buffer(0)
|
, msaa_buffer(0)
|
||||||
, actual_samples(0)
|
, actual_samples(0)
|
||||||
, texture_memory(0)
|
, texture_memory(0)
|
||||||
{
|
{
|
||||||
this->width = width;
|
this->width = settings.width;
|
||||||
this->height = height;
|
this->height = settings.height;
|
||||||
this->pixelWidth = (int) ((width * settings.pixeldensity) + 0.5);
|
this->pixelWidth = (int) ((width * settings.pixeldensity) + 0.5);
|
||||||
this->pixelHeight = (int) ((height * settings.pixeldensity) + 0.5);
|
this->pixelHeight = (int) ((height * settings.pixeldensity) + 0.5);
|
||||||
|
|
||||||
// Vertices are ordered for use with triangle strips:
|
if (texType == TEXTURE_VOLUME)
|
||||||
// 0---2
|
this->depth = settings.layers;
|
||||||
// | / |
|
else if (texType == TEXTURE_2D_ARRAY)
|
||||||
// 1---3
|
this->layers = settings.layers;
|
||||||
// world coordinates
|
else
|
||||||
vertices[0].x = 0;
|
this->layers = 1;
|
||||||
vertices[0].y = 0;
|
|
||||||
vertices[1].x = 0;
|
|
||||||
vertices[1].y = (float) height;
|
|
||||||
vertices[2].x = (float) width;
|
|
||||||
vertices[2].y = 0;
|
|
||||||
vertices[3].x = (float) width;
|
|
||||||
vertices[3].y = (float) height;
|
|
||||||
|
|
||||||
// texture coordinates
|
if (width <= 0 || height <= 0 || layers <= 0)
|
||||||
vertices[0].s = 0;
|
throw love::Exception("Canvas dimensions must be greater than 0.");
|
||||||
vertices[0].t = 0;
|
|
||||||
vertices[1].s = 0;
|
if (texType != TEXTURE_2D && settings.msaa > 1)
|
||||||
vertices[1].t = 1;
|
throw love::Exception("MSAA is only supported for Canvases with the 2D texture type.");
|
||||||
vertices[2].s = 1;
|
|
||||||
vertices[2].t = 0;
|
|
||||||
vertices[3].s = 1;
|
|
||||||
vertices[3].t = 1;
|
|
||||||
|
|
||||||
this->format = getSizedFormat(settings.format);
|
this->format = getSizedFormat(settings.format);
|
||||||
|
|
||||||
|
initVertices();
|
||||||
loadVolatile();
|
loadVolatile();
|
||||||
|
|
||||||
|
if (status != GL_FRAMEBUFFER_COMPLETE)
|
||||||
|
throw love::Exception("Cannot create Canvas: %s", OpenGL::framebufferStatusString(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
Canvas::~Canvas()
|
Canvas::~Canvas()
|
||||||
@@ -148,41 +157,91 @@ bool Canvas::loadVolatile()
|
|||||||
if (texture != 0)
|
if (texture != 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (!Canvas::isSupported())
|
||||||
|
throw love::Exception("Canvases are not supported by your OpenGL drivers!");
|
||||||
|
|
||||||
|
if (!Canvas::isFormatSupported(format))
|
||||||
|
{
|
||||||
|
const char *fstr = "rgba8";
|
||||||
|
love::getConstant(Canvas::getSizedFormat(format), fstr);
|
||||||
|
throw love::Exception("The %s canvas format is not supported by your OpenGL drivers.", fstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.msaa > 1 && texType != TEXTURE_2D)
|
||||||
|
throw love::Exception("MSAA is only supported for 2D texture types.");
|
||||||
|
|
||||||
|
if (!gl.isTextureTypeSupported(texType))
|
||||||
|
{
|
||||||
|
const char *textypestr = "unknown";
|
||||||
|
getConstant(texType, textypestr);
|
||||||
|
throw love::Exception("%s textures are not supported on this system!", textypestr);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (texType)
|
||||||
|
{
|
||||||
|
case TEXTURE_2D:
|
||||||
|
if (pixelWidth > gl.getMax2DTextureSize())
|
||||||
|
throw TextureTooLargeException("width", pixelWidth);
|
||||||
|
else if (pixelHeight > gl.getMax2DTextureSize())
|
||||||
|
throw TextureTooLargeException("height", pixelHeight);
|
||||||
|
break;
|
||||||
|
case TEXTURE_VOLUME:
|
||||||
|
if (pixelWidth > gl.getMax3DTextureSize())
|
||||||
|
throw TextureTooLargeException("width", pixelWidth);
|
||||||
|
else if (pixelHeight > gl.getMax3DTextureSize())
|
||||||
|
throw TextureTooLargeException("height", pixelHeight);
|
||||||
|
else if (depth > gl.getMax3DTextureSize())
|
||||||
|
throw TextureTooLargeException("depth", depth);
|
||||||
|
break;
|
||||||
|
case TEXTURE_2D_ARRAY:
|
||||||
|
if (pixelWidth > gl.getMax2DTextureSize())
|
||||||
|
throw TextureTooLargeException("width", pixelWidth);
|
||||||
|
else if (pixelHeight > gl.getMax2DTextureSize())
|
||||||
|
throw TextureTooLargeException("height", pixelHeight);
|
||||||
|
else if (layers > gl.getMaxTextureLayers())
|
||||||
|
throw TextureTooLargeException("array layer count", layers);
|
||||||
|
break;
|
||||||
|
case TEXTURE_CUBE:
|
||||||
|
if (pixelWidth != pixelHeight)
|
||||||
|
throw love::Exception("Cubemap textures must have equal width and height.");
|
||||||
|
else if (pixelWidth > gl.getMaxCubeTextureSize())
|
||||||
|
throw TextureTooLargeException("width", pixelWidth);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
OpenGL::TempDebugGroup debuggroup("Canvas load");
|
OpenGL::TempDebugGroup debuggroup("Canvas load");
|
||||||
|
|
||||||
fbo = texture = 0;
|
fbo = texture = 0;
|
||||||
msaa_buffer = 0;
|
msaa_buffer = 0;
|
||||||
status = GL_FRAMEBUFFER_COMPLETE;
|
status = GL_FRAMEBUFFER_COMPLETE;
|
||||||
|
|
||||||
// glTexImage2D is guaranteed to error in this case.
|
|
||||||
if (pixelWidth > gl.getMaxTextureSize() || pixelHeight > gl.getMaxTextureSize())
|
|
||||||
{
|
|
||||||
status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// getMaxRenderbufferSamples will be 0 on systems that don't support
|
// getMaxRenderbufferSamples will be 0 on systems that don't support
|
||||||
// multisampled renderbuffers / don't export FBO multisample extensions.
|
// multisampled renderbuffers / don't export FBO multisample extensions.
|
||||||
settings.msaa = std::min(settings.msaa, gl.getMaxRenderbufferSamples());
|
settings.msaa = std::min(settings.msaa, gl.getMaxRenderbufferSamples());
|
||||||
settings.msaa = std::max(settings.msaa, 0);
|
settings.msaa = std::max(settings.msaa, 0);
|
||||||
|
|
||||||
glGenTextures(1, &texture);
|
glGenTextures(1, &texture);
|
||||||
gl.bindTextureToUnit(texture, 0, false);
|
gl.bindTextureToUnit(this, 0, false);
|
||||||
|
|
||||||
|
GLenum gltype = OpenGL::getGLTextureType(texType);
|
||||||
|
|
||||||
if (GLAD_ANGLE_texture_usage)
|
if (GLAD_ANGLE_texture_usage)
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
|
glTexParameteri(gltype, GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
|
||||||
|
|
||||||
setFilter(filter);
|
setFilter(filter);
|
||||||
setWrap(wrap);
|
setWrap(wrap);
|
||||||
|
|
||||||
bool unusedSRGB = false;
|
|
||||||
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, false, unusedSRGB);
|
|
||||||
|
|
||||||
while (glGetError() != GL_NO_ERROR)
|
while (glGetError() != GL_NO_ERROR)
|
||||||
/* Clear the error buffer. */;
|
/* Clear the error buffer. */;
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, fmt.internalformat, pixelWidth, pixelHeight,
|
bool isSRGB = format == PIXELFORMAT_sRGBA8;
|
||||||
0, fmt.externalformat, fmt.type, nullptr);
|
if (!gl.rawTexStorage(texType, 1, format, isSRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers))
|
||||||
|
{
|
||||||
|
status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (glGetError() != GL_NO_ERROR)
|
if (glGetError() != GL_NO_ERROR)
|
||||||
{
|
{
|
||||||
@@ -193,7 +252,7 @@ bool Canvas::loadVolatile()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a canvas-local FBO used for glReadPixels as well as MSAA blitting.
|
// Create a canvas-local FBO used for glReadPixels as well as MSAA blitting.
|
||||||
status = createFBO(fbo, texture);
|
status = createFBO(fbo, texType, texture, texType == TEXTURE_VOLUME ? depth : layers, true);
|
||||||
|
|
||||||
if (status != GL_FRAMEBUFFER_COMPLETE)
|
if (status != GL_FRAMEBUFFER_COMPLETE)
|
||||||
{
|
{
|
||||||
@@ -207,7 +266,7 @@ bool Canvas::loadVolatile()
|
|||||||
|
|
||||||
actual_samples = settings.msaa == 1 ? 0 : settings.msaa;
|
actual_samples = settings.msaa == 1 ? 0 : settings.msaa;
|
||||||
|
|
||||||
if (actual_samples > 0 && !createMSAABuffer(width, height, actual_samples, format, msaa_buffer))
|
if (actual_samples > 0 && !createMSAABuffer(pixelWidth, pixelHeight, actual_samples, format, msaa_buffer))
|
||||||
actual_samples = 0;
|
actual_samples = 0;
|
||||||
|
|
||||||
size_t prevmemsize = texture_memory;
|
size_t prevmemsize = texture_memory;
|
||||||
@@ -246,49 +305,66 @@ void Canvas::setFilter(const Texture::Filter &f)
|
|||||||
throw love::Exception("Invalid texture filter.");
|
throw love::Exception("Invalid texture filter.");
|
||||||
|
|
||||||
filter = f;
|
filter = f;
|
||||||
gl.bindTextureToUnit(texture, 0, false);
|
gl.bindTextureToUnit(this, 0, false);
|
||||||
gl.setTextureFilter(filter);
|
gl.setTextureFilter(texType, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Canvas::setWrap(const Texture::Wrap &w)
|
bool Canvas::setWrap(const Texture::Wrap &w)
|
||||||
{
|
{
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
bool forceclamp = texType == TEXTURE_CUBE;
|
||||||
wrap = w;
|
wrap = w;
|
||||||
|
|
||||||
|
// If we only have limited NPOT support then the wrap mode must be CLAMP.
|
||||||
if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
|
if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
|
||||||
&& (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight)))
|
&& (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight) || depth != nextP2(depth)))
|
||||||
{
|
{
|
||||||
if (wrap.s != WRAP_CLAMP || wrap.t != WRAP_CLAMP)
|
forceclamp = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forceclamp)
|
||||||
|
{
|
||||||
|
if (wrap.s != WRAP_CLAMP || wrap.t != WRAP_CLAMP || wrap.r != WRAP_CLAMP)
|
||||||
success = false;
|
success = false;
|
||||||
|
|
||||||
// If we only have limited NPOT support then the wrap mode must be CLAMP.
|
wrap.s = wrap.t = wrap.r = WRAP_CLAMP;
|
||||||
wrap.s = wrap.t = WRAP_CLAMP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gl.isClampZeroTextureWrapSupported())
|
if (!gl.isClampZeroTextureWrapSupported())
|
||||||
{
|
{
|
||||||
if (wrap.s == WRAP_CLAMP_ZERO)
|
if (wrap.s == WRAP_CLAMP_ZERO) wrap.s = WRAP_CLAMP;
|
||||||
wrap.s = WRAP_CLAMP;
|
if (wrap.t == WRAP_CLAMP_ZERO) wrap.t = WRAP_CLAMP;
|
||||||
if (wrap.t == WRAP_CLAMP_ZERO)
|
if (wrap.r == WRAP_CLAMP_ZERO) wrap.r = WRAP_CLAMP;
|
||||||
wrap.t = WRAP_CLAMP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.bindTextureToUnit(texture, 0, false);
|
gl.bindTextureToUnit(this, 0, false);
|
||||||
gl.setTextureWrap(wrap);
|
gl.setTextureWrap(texType, wrap);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Canvas::setMipmapSharpness(float /*sharpness*/)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ptrdiff_t Canvas::getHandle() const
|
ptrdiff_t Canvas::getHandle() const
|
||||||
{
|
{
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
love::image::ImageData *Canvas::newImageData(love::image::Image *module, int x, int y, int w, int h)
|
love::image::ImageData *Canvas::newImageData(love::image::Image *module, int slice, int x, int y, int w, int h)
|
||||||
{
|
{
|
||||||
if (x < 0 || y < 0 || w <= 0 || h <= 0 || (x + w) > getPixelWidth() || (y + h) > getPixelHeight())
|
if (x < 0 || y < 0 || w <= 0 || h <= 0 || (x + w) > getPixelWidth() || (y + h) > getPixelHeight())
|
||||||
throw love::Exception("Invalid rectangle dimensions.");
|
throw love::Exception("Invalid rectangle dimensions.");
|
||||||
|
|
||||||
|
if (slice < 0 || (texType == TEXTURE_VOLUME && slice >= depth)
|
||||||
|
|| (texType == TEXTURE_2D_ARRAY && slice >= layers)
|
||||||
|
|| (texType == TEXTURE_CUBE && slice >= 6))
|
||||||
|
{
|
||||||
|
throw love::Exception("Invalid slice index.");
|
||||||
|
}
|
||||||
|
|
||||||
Graphics *gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
Graphics *gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||||
if (gfx != nullptr && gfx->isCanvasActive(this))
|
if (gfx != nullptr && gfx->isCanvasActive(this))
|
||||||
throw love::Exception("Canvas:newImageData cannot be called while that Canvas is currently active.");
|
throw love::Exception("Canvas:newImageData cannot be called while that Canvas is currently active.");
|
||||||
@@ -323,8 +399,18 @@ love::image::ImageData *Canvas::newImageData(love::image::Image *module, int x,
|
|||||||
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
|
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
|
||||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getFBO());
|
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getFBO());
|
||||||
|
|
||||||
|
if (slice > 0)
|
||||||
|
{
|
||||||
|
int layer = texType == TEXTURE_CUBE ? 0 : slice;
|
||||||
|
int face = texType == TEXTURE_CUBE ? slice : 0;
|
||||||
|
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, 0, layer, face);
|
||||||
|
}
|
||||||
|
|
||||||
glReadPixels(x, y, w, h, fmt.externalformat, fmt.type, imagedata->getData());
|
glReadPixels(x, y, w, h, fmt.externalformat, fmt.type, imagedata->getData());
|
||||||
|
|
||||||
|
if (slice > 0)
|
||||||
|
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, 0, 0, 0);
|
||||||
|
|
||||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
|
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
|
||||||
|
|
||||||
return imagedata;
|
return imagedata;
|
||||||
@@ -383,14 +469,14 @@ bool Canvas::isFormatSupported(PixelFormat format)
|
|||||||
|
|
||||||
GLuint texture = 0;
|
GLuint texture = 0;
|
||||||
glGenTextures(1, &texture);
|
glGenTextures(1, &texture);
|
||||||
gl.bindTextureToUnit(texture, 0, false);
|
gl.bindTextureToUnit(TEXTURE_2D, texture, 0, false);
|
||||||
|
|
||||||
Texture::Filter f;
|
Texture::Filter f;
|
||||||
f.min = f.mag = Texture::FILTER_NEAREST;
|
f.min = f.mag = Texture::FILTER_NEAREST;
|
||||||
gl.setTextureFilter(f);
|
gl.setTextureFilter(TEXTURE_2D, f);
|
||||||
|
|
||||||
Texture::Wrap w;
|
Texture::Wrap w;
|
||||||
gl.setTextureWrap(w);
|
gl.setTextureWrap(TEXTURE_2D, w);
|
||||||
|
|
||||||
bool unusedSRGB = false;
|
bool unusedSRGB = false;
|
||||||
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, false, unusedSRGB);
|
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, false, unusedSRGB);
|
||||||
@@ -398,7 +484,7 @@ bool Canvas::isFormatSupported(PixelFormat format)
|
|||||||
glTexImage2D(GL_TEXTURE_2D, 0, fmt.internalformat, 2, 2, 0, fmt.externalformat, fmt.type, nullptr);
|
glTexImage2D(GL_TEXTURE_2D, 0, fmt.internalformat, 2, 2, 0, fmt.externalformat, fmt.type, nullptr);
|
||||||
|
|
||||||
GLuint fbo = 0;
|
GLuint fbo = 0;
|
||||||
supported = (createFBO(fbo, texture) == GL_FRAMEBUFFER_COMPLETE);
|
supported = (createFBO(fbo, TEXTURE_2D, texture, 1, false) == GL_FRAMEBUFFER_COMPLETE);
|
||||||
gl.deleteFramebuffer(fbo);
|
gl.deleteFramebuffer(fbo);
|
||||||
|
|
||||||
gl.deleteTexture(texture);
|
gl.deleteTexture(texture);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class Canvas final : public love::graphics::Canvas, public Volatile
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Canvas(int width, int height, const Settings &settings);
|
Canvas(const Settings &settings);
|
||||||
virtual ~Canvas();
|
virtual ~Canvas();
|
||||||
|
|
||||||
// Implements Volatile.
|
// Implements Volatile.
|
||||||
@@ -49,9 +49,10 @@ public:
|
|||||||
// Implements Texture.
|
// Implements Texture.
|
||||||
void setFilter(const Texture::Filter &f) override;
|
void setFilter(const Texture::Filter &f) override;
|
||||||
bool setWrap(const Texture::Wrap &w) override;
|
bool setWrap(const Texture::Wrap &w) override;
|
||||||
|
bool setMipmapSharpness(float sharpness) override;
|
||||||
ptrdiff_t getHandle() const override;
|
ptrdiff_t getHandle() const override;
|
||||||
|
|
||||||
love::image::ImageData *newImageData(love::image::Image *module, int x, int y, int w, int h) override;
|
love::image::ImageData *newImageData(love::image::Image *module, int slice, int x, int y, int w, int h) override;
|
||||||
|
|
||||||
int getMSAA() const override
|
int getMSAA() const override
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,190 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2006-2017 LOVE Development Team
|
|
||||||
*
|
|
||||||
* This software is provided 'as-is', without any express or implied
|
|
||||||
* warranty. In no event will the authors be held liable for any damages
|
|
||||||
* arising from the use of this software.
|
|
||||||
*
|
|
||||||
* Permission is granted to anyone to use this software for any purpose,
|
|
||||||
* including commercial applications, and to alter it and redistribute it
|
|
||||||
* freely, subject to the following restrictions:
|
|
||||||
*
|
|
||||||
* 1. The origin of this software must not be misrepresented; you must not
|
|
||||||
* claim that you wrote the original software. If you use this software
|
|
||||||
* in a product, an acknowledgment in the product documentation would be
|
|
||||||
* appreciated but is not required.
|
|
||||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
||||||
* misrepresented as being the original software.
|
|
||||||
* 3. This notice may not be removed or altered from any source distribution.
|
|
||||||
**/
|
|
||||||
|
|
||||||
// LOVE
|
|
||||||
#include "Font.h"
|
|
||||||
#include "graphics/Graphics.h"
|
|
||||||
|
|
||||||
namespace love
|
|
||||||
{
|
|
||||||
namespace graphics
|
|
||||||
{
|
|
||||||
namespace opengl
|
|
||||||
{
|
|
||||||
|
|
||||||
Font::Font(love::font::Rasterizer *r, const Texture::Filter &f)
|
|
||||||
: love::graphics::Font(r, f)
|
|
||||||
, textureMemorySize(0)
|
|
||||||
{
|
|
||||||
loadVolatile();
|
|
||||||
}
|
|
||||||
|
|
||||||
Font::~Font()
|
|
||||||
{
|
|
||||||
unloadVolatile();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Font::createTexture()
|
|
||||||
{
|
|
||||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
|
||||||
gfx->flushStreamDraws();
|
|
||||||
|
|
||||||
OpenGL::TempDebugGroup debuggroup("Font create texture");
|
|
||||||
|
|
||||||
size_t bpp = getPixelFormatSize(pixelFormat);
|
|
||||||
|
|
||||||
size_t prevmemsize = textureMemorySize;
|
|
||||||
if (prevmemsize > 0)
|
|
||||||
{
|
|
||||||
textureMemorySize -= (textureWidth * textureHeight * bpp);
|
|
||||||
gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
|
|
||||||
}
|
|
||||||
|
|
||||||
GLuint t = 0;
|
|
||||||
TextureSize size = {textureWidth, textureHeight};
|
|
||||||
TextureSize nextsize = getNextTextureSize();
|
|
||||||
bool recreatetexture = false;
|
|
||||||
|
|
||||||
// If we have an existing texture already, we'll try replacing it with a
|
|
||||||
// larger-sized one rather than creating a second one. Having a single
|
|
||||||
// texture reduces texture switches and draw calls when rendering.
|
|
||||||
if ((nextsize.width > size.width || nextsize.height > size.height)
|
|
||||||
&& !textures.empty())
|
|
||||||
{
|
|
||||||
recreatetexture = true;
|
|
||||||
size = nextsize;
|
|
||||||
t = textures.back();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
glGenTextures(1, &t);
|
|
||||||
|
|
||||||
gl.bindTextureToUnit(t, 0, false);
|
|
||||||
|
|
||||||
gl.setTextureFilter(filter);
|
|
||||||
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
|
|
||||||
bool sRGB = isGammaCorrect();
|
|
||||||
OpenGL::TextureFormat fmt = gl.convertPixelFormat(pixelFormat, false, sRGB);
|
|
||||||
|
|
||||||
if (fmt.swizzled)
|
|
||||||
{
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, fmt.swizzle[0]);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, fmt.swizzle[1]);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, fmt.swizzle[2]);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, fmt.swizzle[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the texture with transparent black.
|
|
||||||
std::vector<GLubyte> emptydata(size.width * size.height * bpp, 0);
|
|
||||||
|
|
||||||
// Clear errors before initializing.
|
|
||||||
while (glGetError() != GL_NO_ERROR);
|
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, fmt.internalformat, size.width, size.height,
|
|
||||||
0, fmt.externalformat, fmt.type, &emptydata[0]);
|
|
||||||
|
|
||||||
if (glGetError() != GL_NO_ERROR)
|
|
||||||
{
|
|
||||||
if (!recreatetexture)
|
|
||||||
gl.deleteTexture(t);
|
|
||||||
throw love::Exception("Could not create font texture!");
|
|
||||||
}
|
|
||||||
|
|
||||||
textureWidth = size.width;
|
|
||||||
textureHeight = size.height;
|
|
||||||
|
|
||||||
rowHeight = textureX = textureY = TEXTURE_PADDING;
|
|
||||||
|
|
||||||
prevmemsize = textureMemorySize;
|
|
||||||
textureMemorySize += emptydata.size();
|
|
||||||
gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
|
|
||||||
|
|
||||||
// Re-add the old glyphs if we re-created the existing texture object.
|
|
||||||
if (recreatetexture)
|
|
||||||
{
|
|
||||||
textureCacheID++;
|
|
||||||
|
|
||||||
std::vector<uint32> glyphstoadd;
|
|
||||||
|
|
||||||
for (const auto &glyphpair : glyphs)
|
|
||||||
glyphstoadd.push_back(glyphpair.first);
|
|
||||||
|
|
||||||
glyphs.clear();
|
|
||||||
|
|
||||||
for (uint32 g : glyphstoadd)
|
|
||||||
addGlyph(g);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
textures.push_back(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Font::uploadGlyphToTexture(font::GlyphData *gd, Glyph &glyph)
|
|
||||||
{
|
|
||||||
bool isSRGB = isGammaCorrect();
|
|
||||||
OpenGL::TextureFormat fmt = gl.convertPixelFormat(pixelFormat, false, isSRGB);
|
|
||||||
|
|
||||||
glyph.texture = textures.back();
|
|
||||||
|
|
||||||
gl.bindTextureToUnit(glyph.texture, 0, false);
|
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, textureX, textureY, gd->getWidth(), gd->getHeight(),
|
|
||||||
fmt.externalformat, fmt.type, gd->getData());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Font::setFilter(const Texture::Filter &f)
|
|
||||||
{
|
|
||||||
if (!Texture::validateFilter(f, false))
|
|
||||||
throw love::Exception("Invalid texture filter.");
|
|
||||||
|
|
||||||
filter = f;
|
|
||||||
|
|
||||||
for (GLuint texture : textures)
|
|
||||||
{
|
|
||||||
gl.bindTextureToUnit(texture, 0, false);
|
|
||||||
gl.setTextureFilter(filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Font::loadVolatile()
|
|
||||||
{
|
|
||||||
createTexture();
|
|
||||||
textureCacheID++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Font::unloadVolatile()
|
|
||||||
{
|
|
||||||
// nuke everything from orbit
|
|
||||||
|
|
||||||
glyphs.clear();
|
|
||||||
|
|
||||||
for (GLuint texture : textures)
|
|
||||||
gl.deleteTexture(texture);
|
|
||||||
|
|
||||||
textures.clear();
|
|
||||||
|
|
||||||
gl.updateTextureMemorySize(textureMemorySize, 0);
|
|
||||||
textureMemorySize = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // opengl
|
|
||||||
} // graphics
|
|
||||||
} // love
|
|
||||||
@@ -25,12 +25,10 @@
|
|||||||
|
|
||||||
#include "Graphics.h"
|
#include "Graphics.h"
|
||||||
#include "font/Font.h"
|
#include "font/Font.h"
|
||||||
#include "Font.h"
|
|
||||||
#include "StreamBuffer.h"
|
#include "StreamBuffer.h"
|
||||||
#include "math/MathModule.h"
|
#include "math/MathModule.h"
|
||||||
#include "window/Window.h"
|
#include "window/Window.h"
|
||||||
#include "Buffer.h"
|
#include "Buffer.h"
|
||||||
#include "Video.h"
|
|
||||||
#include "Text.h"
|
#include "Text.h"
|
||||||
|
|
||||||
#include "libraries/xxHash/xxhash.h"
|
#include "libraries/xxHash/xxhash.h"
|
||||||
@@ -103,19 +101,14 @@ love::graphics::StreamBuffer *Graphics::newStreamBuffer(BufferType type, size_t
|
|||||||
return CreateStreamBuffer(type, size);
|
return CreateStreamBuffer(type, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
love::graphics::Image *Graphics::newImage(const std::vector<love::image::ImageData *> &data, const Image::Settings &settings)
|
love::graphics::Image *Graphics::newImage(const Image::Slices &data, const Image::Settings &settings)
|
||||||
{
|
{
|
||||||
return new Image(data, settings);
|
return new Image(data, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
love::graphics::Image *Graphics::newImage(const std::vector<love::image::CompressedImageData *> &cdata, const Image::Settings &settings)
|
love::graphics::Image *Graphics::newImage(TextureType textype, PixelFormat format, int width, int height, int slices, const Image::Settings &settings)
|
||||||
{
|
{
|
||||||
return new Image(cdata, settings);
|
return new Image(textype, format, width, height, slices, settings);
|
||||||
}
|
|
||||||
|
|
||||||
graphics::Font *Graphics::newFont(love::font::Rasterizer *r, const Texture::Filter &filter)
|
|
||||||
{
|
|
||||||
return new Font(r, filter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
love::graphics::SpriteBatch *Graphics::newSpriteBatch(Texture *texture, int size, vertex::Usage usage)
|
love::graphics::SpriteBatch *Graphics::newSpriteBatch(Texture *texture, int size, vertex::Usage usage)
|
||||||
@@ -128,35 +121,12 @@ love::graphics::ParticleSystem *Graphics::newParticleSystem(Texture *texture, in
|
|||||||
return new ParticleSystem(this, texture, size);
|
return new ParticleSystem(this, texture, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
love::graphics::Canvas *Graphics::newCanvas(int width, int height, const Canvas::Settings &settings)
|
love::graphics::Canvas *Graphics::newCanvas(const Canvas::Settings &settings)
|
||||||
{
|
{
|
||||||
if (!Canvas::isSupported())
|
return new Canvas(settings);
|
||||||
throw love::Exception("Canvases are not supported by your OpenGL drivers!");
|
|
||||||
|
|
||||||
if (!Canvas::isFormatSupported(settings.format))
|
|
||||||
{
|
|
||||||
const char *fstr = "rgba8";
|
|
||||||
love::getConstant(Canvas::getSizedFormat(settings.format), fstr);
|
|
||||||
throw love::Exception("The %s canvas format is not supported by your OpenGL drivers.", fstr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (width > gl.getMaxTextureSize())
|
|
||||||
throw Exception("Cannot create canvas: width of %d pixels is too large for this system.", width);
|
|
||||||
else if (height > gl.getMaxTextureSize())
|
|
||||||
throw Exception("Cannot create canvas: height of %d pixels is too large for this system.", height);
|
|
||||||
|
|
||||||
Canvas *canvas = new Canvas(width, height, settings);
|
|
||||||
GLenum err = canvas->getStatus();
|
|
||||||
|
|
||||||
// everything ok, return canvas (early out)
|
|
||||||
if (err == GL_FRAMEBUFFER_COMPLETE)
|
|
||||||
return canvas;
|
|
||||||
|
|
||||||
canvas->release();
|
|
||||||
throw love::Exception("Cannot create Canvas: %s", OpenGL::framebufferStatusString(err));
|
|
||||||
return nullptr; // never reached
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
love::graphics::Shader *Graphics::newShader(const Shader::ShaderSource &source)
|
love::graphics::Shader *Graphics::newShader(const Shader::ShaderSource &source)
|
||||||
{
|
{
|
||||||
return new Shader(source);
|
return new Shader(source);
|
||||||
@@ -192,11 +162,6 @@ love::graphics::Text *Graphics::newText(graphics::Font *font, const std::vector<
|
|||||||
return new Text(this, font, text);
|
return new Text(this, font, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
love::graphics::Video *Graphics::newVideo(love::video::VideoStream *stream, float pixeldensity)
|
|
||||||
{
|
|
||||||
return new Video(stream, pixeldensity);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight)
|
void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight)
|
||||||
{
|
{
|
||||||
this->width = width;
|
this->width = width;
|
||||||
@@ -204,7 +169,7 @@ void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelh
|
|||||||
this->pixelWidth = pixelwidth;
|
this->pixelWidth = pixelwidth;
|
||||||
this->pixelHeight = pixelheight;
|
this->pixelHeight = pixelheight;
|
||||||
|
|
||||||
if (states.back().canvases.empty())
|
if (states.back().renderTargets.empty())
|
||||||
{
|
{
|
||||||
// Set the viewport to top-left corner.
|
// Set the viewport to top-left corner.
|
||||||
gl.setViewport({0, 0, pixelwidth, pixelheight});
|
gl.setViewport({0, 0, pixelwidth, pixelheight});
|
||||||
@@ -262,6 +227,10 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
|
|||||||
// Set pixel row alignment
|
// Set pixel row alignment
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
|
|
||||||
|
// Always enable seamless cubemap filtering when possible.
|
||||||
|
if (GLAD_VERSION_3_2 || GLAD_ARB_seamless_cube_map)
|
||||||
|
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
|
||||||
|
|
||||||
// Set whether drawing converts input from linear -> sRGB colorspace.
|
// Set whether drawing converts input from linear -> sRGB colorspace.
|
||||||
if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB
|
if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB
|
||||||
|| GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB)
|
|| GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB)
|
||||||
@@ -374,6 +343,9 @@ void Graphics::flushStreamDraws()
|
|||||||
if (sbstate.vertexCount == 0 && sbstate.indexCount == 0)
|
if (sbstate.vertexCount == 0 && sbstate.indexCount == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (Shader::current && sbstate.texture.get())
|
||||||
|
Shader::current->checkMainTextureType(sbstate.texture->getTextureType());
|
||||||
|
|
||||||
OpenGL::TempDebugGroup debuggroup("Stream vertices flush and draw");
|
OpenGL::TempDebugGroup debuggroup("Stream vertices flush and draw");
|
||||||
|
|
||||||
uint32 attribs = 0;
|
uint32 attribs = 0;
|
||||||
@@ -448,10 +420,6 @@ void Graphics::flushStreamDraws()
|
|||||||
pushIdentityTransform();
|
pushIdentityTransform();
|
||||||
|
|
||||||
gl.prepareDraw();
|
gl.prepareDraw();
|
||||||
|
|
||||||
if (sbstate.textureHandle != 0)
|
|
||||||
gl.bindTextureToUnit((GLuint) sbstate.textureHandle, 0, false);
|
|
||||||
else
|
|
||||||
gl.bindTextureToUnit(sbstate.texture, 0, false);
|
gl.bindTextureToUnit(sbstate.texture, 0, false);
|
||||||
|
|
||||||
gl.useVertexAttribArrays(attribs);
|
gl.useVertexAttribArrays(attribs);
|
||||||
@@ -547,21 +515,22 @@ void Graphics::setDebug(bool enable)
|
|||||||
::printf("OpenGL debug output enabled (LOVE_GRAPHICS_DEBUG=1)\n");
|
::printf("OpenGL debug output enabled (LOVE_GRAPHICS_DEBUG=1)\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::setCanvas(const std::vector<love::graphics::Canvas *> &canvases)
|
void Graphics::setCanvas(const std::vector<RenderTarget> &rts)
|
||||||
{
|
{
|
||||||
DisplayState &state = states.back();
|
DisplayState &state = states.back();
|
||||||
int ncanvases = (int) canvases.size();
|
int ncanvases = (int) rts.size();
|
||||||
|
|
||||||
if (ncanvases == 0)
|
if (ncanvases == 0)
|
||||||
return setCanvas();
|
return setCanvas();
|
||||||
|
|
||||||
if (ncanvases == (int) state.canvases.size())
|
if (ncanvases == (int) state.renderTargets.size())
|
||||||
{
|
{
|
||||||
bool modified = false;
|
bool modified = false;
|
||||||
|
|
||||||
for (int i = 0; i < ncanvases; i++)
|
for (int i = 0; i < ncanvases; i++)
|
||||||
{
|
{
|
||||||
if (canvases[i] != state.canvases[i].get())
|
if (rts[i].canvas != state.renderTargets[i].canvas.get()
|
||||||
|
|| rts[i].slice != state.renderTargets[i].slice)
|
||||||
{
|
{
|
||||||
modified = true;
|
modified = true;
|
||||||
break;
|
break;
|
||||||
@@ -575,7 +544,7 @@ void Graphics::setCanvas(const std::vector<love::graphics::Canvas *> &canvases)
|
|||||||
if (ncanvases > gl.getMaxRenderTargets())
|
if (ncanvases > gl.getMaxRenderTargets())
|
||||||
throw love::Exception("This system can't simultaneously render to %d canvases.", ncanvases);
|
throw love::Exception("This system can't simultaneously render to %d canvases.", ncanvases);
|
||||||
|
|
||||||
love::graphics::Canvas *firstcanvas = canvases[0];
|
love::graphics::Canvas *firstcanvas = rts[0].canvas;
|
||||||
|
|
||||||
bool multiformatsupported = Canvas::isMultiFormatMultiCanvasSupported();
|
bool multiformatsupported = Canvas::isMultiFormatMultiCanvasSupported();
|
||||||
PixelFormat firstformat = firstcanvas->getPixelFormat();
|
PixelFormat firstformat = firstcanvas->getPixelFormat();
|
||||||
@@ -586,7 +555,7 @@ void Graphics::setCanvas(const std::vector<love::graphics::Canvas *> &canvases)
|
|||||||
|
|
||||||
for (int i = 1; i < ncanvases; i++)
|
for (int i = 1; i < ncanvases; i++)
|
||||||
{
|
{
|
||||||
love::graphics::Canvas *c = canvases[i];
|
love::graphics::Canvas *c = rts[i].canvas;
|
||||||
|
|
||||||
if (c->getPixelWidth() != pixelwidth || c->getPixelHeight() != pixelheight)
|
if (c->getPixelWidth() != pixelwidth || c->getPixelHeight() != pixelheight)
|
||||||
throw love::Exception("All canvases in must have the same pixel dimensions.");
|
throw love::Exception("All canvases in must have the same pixel dimensions.");
|
||||||
@@ -595,7 +564,7 @@ void Graphics::setCanvas(const std::vector<love::graphics::Canvas *> &canvases)
|
|||||||
throw love::Exception("This system doesn't support multi-canvas rendering with different canvas formats.");
|
throw love::Exception("This system doesn't support multi-canvas rendering with different canvas formats.");
|
||||||
|
|
||||||
if (c->getRequestedMSAA() != firstcanvas->getRequestedMSAA())
|
if (c->getRequestedMSAA() != firstcanvas->getRequestedMSAA())
|
||||||
throw love::Exception("All Canvases in must have the same requested MSAA value.");
|
throw love::Exception("All Canvases in must have the same MSAA value.");
|
||||||
|
|
||||||
if (c->getPixelFormat() == PIXELFORMAT_sRGBA8)
|
if (c->getPixelFormat() == PIXELFORMAT_sRGBA8)
|
||||||
hasSRGBcanvas = true;
|
hasSRGBcanvas = true;
|
||||||
@@ -605,7 +574,7 @@ void Graphics::setCanvas(const std::vector<love::graphics::Canvas *> &canvases)
|
|||||||
|
|
||||||
endPass();
|
endPass();
|
||||||
|
|
||||||
bindCachedFBO(canvases);
|
bindCachedFBO(rts);
|
||||||
|
|
||||||
gl.setViewport({0, 0, pixelwidth, pixelheight});
|
gl.setViewport({0, 0, pixelwidth, pixelheight});
|
||||||
|
|
||||||
@@ -627,13 +596,13 @@ void Graphics::setCanvas(const std::vector<love::graphics::Canvas *> &canvases)
|
|||||||
gl.setFramebufferSRGB(false);
|
gl.setFramebufferSRGB(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<StrongRef<love::graphics::Canvas>> canvasrefs;
|
std::vector<RenderTargetStrongRef> canvasrefs;
|
||||||
canvasrefs.reserve(canvases.size());
|
canvasrefs.reserve(rts.size());
|
||||||
|
|
||||||
for (love::graphics::Canvas *c : canvases)
|
for (auto c : rts)
|
||||||
canvasrefs.push_back(c);
|
canvasrefs.emplace_back(c.canvas, c.slice);
|
||||||
|
|
||||||
std::swap(state.canvases, canvasrefs);
|
std::swap(state.renderTargets, canvasrefs);
|
||||||
|
|
||||||
canvasSwitchCount++;
|
canvasSwitchCount++;
|
||||||
}
|
}
|
||||||
@@ -642,14 +611,14 @@ void Graphics::setCanvas()
|
|||||||
{
|
{
|
||||||
DisplayState &state = states.back();
|
DisplayState &state = states.back();
|
||||||
|
|
||||||
if (state.canvases.empty())
|
if (state.renderTargets.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
OpenGL::TempDebugGroup debuggroup("setCanvas()");
|
OpenGL::TempDebugGroup debuggroup("setCanvas()");
|
||||||
|
|
||||||
endPass();
|
endPass();
|
||||||
|
|
||||||
state.canvases.clear();
|
state.renderTargets.clear();
|
||||||
|
|
||||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO());
|
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO());
|
||||||
|
|
||||||
@@ -682,17 +651,18 @@ void Graphics::endPass()
|
|||||||
// Discard the stencil buffer.
|
// Discard the stencil buffer.
|
||||||
discard({}, true);
|
discard({}, true);
|
||||||
|
|
||||||
auto &canvases = states.back().canvases;
|
auto &canvases = states.back().renderTargets;
|
||||||
|
|
||||||
// Resolve MSAA buffers.
|
// Resolve MSAA buffers. MSAA is only supported for 2D render targets so we
|
||||||
if (canvases.size() > 0 && canvases[0]->getMSAA() > 1)
|
// don't have to worry about resolving to slices.
|
||||||
|
if (canvases.size() > 0 && canvases[0].canvas->getMSAA() > 1)
|
||||||
{
|
{
|
||||||
int w = canvases[0]->getPixelWidth();
|
int w = canvases[0].canvas->getPixelWidth();
|
||||||
int h = canvases[0]->getPixelHeight();
|
int h = canvases[0].canvas->getPixelHeight();
|
||||||
|
|
||||||
for (int i = 0; i < (int) canvases.size(); i++)
|
for (int i = 0; i < (int) canvases.size(); i++)
|
||||||
{
|
{
|
||||||
Canvas *c = (Canvas *) canvases[i].get();
|
Canvas *c = (Canvas *) canvases[i].canvas.get();
|
||||||
|
|
||||||
glReadBuffer(GL_COLOR_ATTACHMENT0 + i);
|
glReadBuffer(GL_COLOR_ATTACHMENT0 + i);
|
||||||
|
|
||||||
@@ -728,7 +698,7 @@ void Graphics::clear(const std::vector<OptionalColorf> &colors)
|
|||||||
if (colors.size() == 0)
|
if (colors.size() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int ncanvases = (int) states.back().canvases.size();
|
int ncanvases = (int) states.back().renderTargets.size();
|
||||||
int ncolors = std::min((int) colors.size(), ncanvases);
|
int ncolors = std::min((int) colors.size(), ncanvases);
|
||||||
|
|
||||||
if (ncolors <= 1 && ncanvases <= 1)
|
if (ncolors <= 1 && ncanvases <= 1)
|
||||||
@@ -810,7 +780,7 @@ void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector<bool>
|
|||||||
attachments.reserve(colorbuffers.size());
|
attachments.reserve(colorbuffers.size());
|
||||||
|
|
||||||
// glDiscardFramebuffer uses different attachment enums for the default FBO.
|
// glDiscardFramebuffer uses different attachment enums for the default FBO.
|
||||||
if (states.back().canvases.empty() && gl.getDefaultFBO() == 0)
|
if (states.back().renderTargets.empty() && gl.getDefaultFBO() == 0)
|
||||||
{
|
{
|
||||||
if (colorbuffers.size() > 0 && colorbuffers[0])
|
if (colorbuffers.size() > 0 && colorbuffers[0])
|
||||||
attachments.push_back(GL_COLOR);
|
attachments.push_back(GL_COLOR);
|
||||||
@@ -823,7 +793,7 @@ void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector<bool>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int rendertargetcount = std::max((int) states.back().canvases.size(), 1);
|
int rendertargetcount = std::max((int) states.back().renderTargets.size(), 1);
|
||||||
|
|
||||||
for (int i = 0; i < (int) colorbuffers.size(); i++)
|
for (int i = 0; i < (int) colorbuffers.size(); i++)
|
||||||
{
|
{
|
||||||
@@ -845,11 +815,10 @@ void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector<bool>
|
|||||||
glDiscardFramebufferEXT(gltarget, (GLint) attachments.size(), &attachments[0]);
|
glDiscardFramebufferEXT(gltarget, (GLint) attachments.size(), &attachments[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::bindCachedFBO(const std::vector<love::graphics::Canvas *> &canvases)
|
void Graphics::bindCachedFBO(const std::vector<RenderTarget> &targets)
|
||||||
{
|
{
|
||||||
int ncanvases = (int) canvases.size();
|
int ntargets = (int) targets.size();
|
||||||
|
uint32 hash = XXH32(&targets[0], sizeof(RenderTarget) * ntargets, 0);
|
||||||
uint32 hash = XXH32(&canvases[0], sizeof(love::graphics::Canvas *) * ncanvases, 0);
|
|
||||||
|
|
||||||
GLuint fbo = framebufferObjects[hash];
|
GLuint fbo = framebufferObjects[hash];
|
||||||
|
|
||||||
@@ -859,35 +828,40 @@ void Graphics::bindCachedFBO(const std::vector<love::graphics::Canvas *> &canvas
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int w = canvases[0]->getPixelWidth();
|
int w = targets[0].canvas->getPixelWidth();
|
||||||
int h = canvases[0]->getPixelHeight();
|
int h = targets[0].canvas->getPixelHeight();
|
||||||
int msaa = std::max(canvases[0]->getMSAA(), 1);
|
int msaa = std::max(targets[0].canvas->getMSAA(), 1);
|
||||||
|
|
||||||
glGenFramebuffers(1, &fbo);
|
glGenFramebuffers(1, &fbo);
|
||||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
|
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
|
||||||
|
|
||||||
GLenum drawbuffers[MAX_COLOR_RENDER_TARGETS];
|
GLenum drawbuffers[MAX_COLOR_RENDER_TARGETS];
|
||||||
|
|
||||||
for (int i = 0; i < ncanvases; i++)
|
for (int i = 0; i < ntargets; i++)
|
||||||
{
|
{
|
||||||
drawbuffers[i] = GL_COLOR_ATTACHMENT0 + i;
|
drawbuffers[i] = GL_COLOR_ATTACHMENT0 + i;
|
||||||
|
|
||||||
if (msaa > 1)
|
if (msaa > 1)
|
||||||
{
|
{
|
||||||
GLuint rbo = (GLuint) canvases[i]->getMSAAHandle();
|
GLuint rbo = (GLuint) targets[i].canvas->getMSAAHandle();
|
||||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, drawbuffers[i], GL_RENDERBUFFER, rbo);
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, drawbuffers[i], GL_RENDERBUFFER, rbo);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GLuint tex = (GLuint) canvases[i]->getHandle();
|
GLuint tex = (GLuint) targets[i].canvas->getHandle();
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, drawbuffers[i], GL_TEXTURE_2D, tex, 0);
|
TextureType textype = targets[i].canvas->getTextureType();
|
||||||
|
|
||||||
|
int layer = textype == TEXTURE_CUBE ? 0 : targets[i].slice;
|
||||||
|
int face = textype == TEXTURE_CUBE ? targets[i].slice : 0;
|
||||||
|
|
||||||
|
gl.framebufferTexture(drawbuffers[i], textype, tex, 0, layer, face);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ncanvases > 1)
|
if (ntargets > 1)
|
||||||
glDrawBuffers(ncanvases, drawbuffers);
|
glDrawBuffers(ntargets, drawbuffers);
|
||||||
|
|
||||||
GLuint stencil = attachCachedStencilBuffer(w, h, canvases[0]->getRequestedMSAA());
|
GLuint stencil = attachCachedStencilBuffer(w, h, targets[0].canvas->getRequestedMSAA());
|
||||||
|
|
||||||
if (stencil == 0)
|
if (stencil == 0)
|
||||||
{
|
{
|
||||||
@@ -990,7 +964,7 @@ void Graphics::present(void *screenshotCallbackData)
|
|||||||
if (!isActive())
|
if (!isActive())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!states.back().canvases.empty())
|
if (!states.back().renderTargets.empty())
|
||||||
throw love::Exception("present cannot be called while a Canvas is active.");
|
throw love::Exception("present cannot be called while a Canvas is active.");
|
||||||
|
|
||||||
endPass();
|
endPass();
|
||||||
@@ -1029,8 +1003,8 @@ void Graphics::present(void *screenshotCallbackData)
|
|||||||
{
|
{
|
||||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_DRAW, info.info.uikit.resolveFramebuffer);
|
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_DRAW, info.info.uikit.resolveFramebuffer);
|
||||||
|
|
||||||
// We need to do an explicit MSAA resolve on iOS, because it uses GLES
|
// We need to do an explicit MSAA resolve on iOS, because it uses
|
||||||
// FBOs rather than a system framebuffer.
|
// GLES FBOs rather than a system framebuffer.
|
||||||
if (GLAD_ES_VERSION_3_0)
|
if (GLAD_ES_VERSION_3_0)
|
||||||
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||||
else if (GLAD_APPLE_framebuffer_multisample)
|
else if (GLAD_APPLE_framebuffer_multisample)
|
||||||
@@ -1122,7 +1096,7 @@ void Graphics::setScissor(const Rect &rect)
|
|||||||
glrect.h = (int) (rect.h * density);
|
glrect.h = (int) (rect.h * density);
|
||||||
|
|
||||||
// OpenGL's reversed y-coordinate is compensated for in OpenGL::setScissor.
|
// OpenGL's reversed y-coordinate is compensated for in OpenGL::setScissor.
|
||||||
gl.setScissor(glrect, !state.canvases.empty());
|
gl.setScissor(glrect, !state.renderTargets.empty());
|
||||||
|
|
||||||
state.scissor = true;
|
state.scissor = true;
|
||||||
state.scissorRect = rect;
|
state.scissorRect = rect;
|
||||||
@@ -1139,7 +1113,7 @@ void Graphics::setScissor()
|
|||||||
|
|
||||||
void Graphics::drawToStencilBuffer(StencilAction action, int value)
|
void Graphics::drawToStencilBuffer(StencilAction action, int value)
|
||||||
{
|
{
|
||||||
if (states.back().canvases.empty() && !windowHasStencil)
|
if (states.back().renderTargets.empty() && !windowHasStencil)
|
||||||
throw love::Exception("The window must have stenciling enabled to draw to the main screen's stencil buffer.");
|
throw love::Exception("The window must have stenciling enabled to draw to the main screen's stencil buffer.");
|
||||||
|
|
||||||
flushStreamDraws();
|
flushStreamDraws();
|
||||||
@@ -1200,7 +1174,7 @@ void Graphics::stopDrawToStencilBuffer()
|
|||||||
|
|
||||||
void Graphics::setStencilTest(CompareMode compare, int value)
|
void Graphics::setStencilTest(CompareMode compare, int value)
|
||||||
{
|
{
|
||||||
if (compare != COMPARE_ALWAYS && states.back().canvases.empty() && !windowHasStencil)
|
if (compare != COMPARE_ALWAYS && states.back().renderTargets.empty() && !windowHasStencil)
|
||||||
throw love::Exception("The window must have stenciling enabled to use setStencilTest on the main screen.");
|
throw love::Exception("The window must have stenciling enabled to use setStencilTest on the main screen.");
|
||||||
|
|
||||||
DisplayState &state = states.back();
|
DisplayState &state = states.back();
|
||||||
@@ -1454,15 +1428,21 @@ double Graphics::getSystemLimit(SystemLimit limittype) const
|
|||||||
{
|
{
|
||||||
switch (limittype)
|
switch (limittype)
|
||||||
{
|
{
|
||||||
case Graphics::LIMIT_POINT_SIZE:
|
case LIMIT_POINT_SIZE:
|
||||||
return (double) gl.getMaxPointSize();
|
return (double) gl.getMaxPointSize();
|
||||||
case Graphics::LIMIT_TEXTURE_SIZE:
|
case LIMIT_TEXTURE_SIZE:
|
||||||
return (double) gl.getMaxTextureSize();
|
return (double) gl.getMax2DTextureSize();
|
||||||
case Graphics::LIMIT_MULTI_CANVAS:
|
case LIMIT_TEXTURE_LAYERS:
|
||||||
|
return (double) gl.getMaxTextureLayers();
|
||||||
|
case LIMIT_VOLUME_TEXTURE_SIZE:
|
||||||
|
return (double) gl.getMax3DTextureSize();
|
||||||
|
case LIMIT_CUBE_TEXTURE_SIZE:
|
||||||
|
return (double) gl.getMaxCubeTextureSize();
|
||||||
|
case LIMIT_MULTI_CANVAS:
|
||||||
return (double) gl.getMaxRenderTargets();
|
return (double) gl.getMaxRenderTargets();
|
||||||
case Graphics::LIMIT_CANVAS_MSAA:
|
case LIMIT_CANVAS_MSAA:
|
||||||
return (double) gl.getMaxRenderbufferSamples();
|
return (double) gl.getMaxRenderbufferSamples();
|
||||||
case Graphics::LIMIT_ANISOTROPY:
|
case LIMIT_ANISOTROPY:
|
||||||
return (double) gl.getMaxAnisotropy();
|
return (double) gl.getMaxAnisotropy();
|
||||||
default:
|
default:
|
||||||
return 0.0;
|
return 0.0;
|
||||||
@@ -1483,6 +1463,10 @@ bool Graphics::isSupported(Feature feature) const
|
|||||||
return GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot;
|
return GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot;
|
||||||
case FEATURE_PIXEL_SHADER_HIGHP:
|
case FEATURE_PIXEL_SHADER_HIGHP:
|
||||||
return gl.isPixelShaderHighpSupported();
|
return gl.isPixelShaderHighpSupported();
|
||||||
|
case FEATURE_ARRAY_TEXTURE:
|
||||||
|
return gl.isTextureTypeSupported(TEXTURE_2D_ARRAY);
|
||||||
|
case FEATURE_VOLUME_TEXTURE:
|
||||||
|
return gl.isTextureTypeSupported(TEXTURE_VOLUME);
|
||||||
case FEATURE_GLSL3:
|
case FEATURE_GLSL3:
|
||||||
return GLAD_ES_VERSION_3_0 || gl.isCoreProfile();
|
return GLAD_ES_VERSION_3_0 || gl.isCoreProfile();
|
||||||
case FEATURE_INSTANCING:
|
case FEATURE_INSTANCING:
|
||||||
|
|||||||
@@ -61,17 +61,13 @@ public:
|
|||||||
// Implements Module.
|
// Implements Module.
|
||||||
const char *getName() const override;
|
const char *getName() const override;
|
||||||
|
|
||||||
love::graphics::Image *newImage(const std::vector<love::image::ImageData *> &data, const Image::Settings &settings) override;
|
love::graphics::Image *newImage(const Image::Slices &data, const Image::Settings &settings) override;
|
||||||
love::graphics::Image *newImage(const std::vector<love::image::CompressedImageData *> &cdata, const Image::Settings &settings) override;
|
love::graphics::Image *newImage(TextureType textype, PixelFormat format, int width, int height, int slices, const Image::Settings &settings) override;
|
||||||
|
|
||||||
love::graphics::Font *newFont(love::font::Rasterizer *data, const Texture::Filter &filter = Texture::defaultFilter) override;
|
|
||||||
|
|
||||||
love::graphics::SpriteBatch *newSpriteBatch(Texture *texture, int size, vertex::Usage usage) override;
|
love::graphics::SpriteBatch *newSpriteBatch(Texture *texture, int size, vertex::Usage usage) override;
|
||||||
|
|
||||||
love::graphics::ParticleSystem *newParticleSystem(Texture *texture, int size) override;
|
love::graphics::ParticleSystem *newParticleSystem(Texture *texture, int size) override;
|
||||||
|
|
||||||
love::graphics::Canvas *newCanvas(int width, int height, const Canvas::Settings &settings) override;
|
love::graphics::Canvas *newCanvas(const Canvas::Settings &settings) override;
|
||||||
|
|
||||||
love::graphics::Shader *newShader(const Shader::ShaderSource &source) override;
|
love::graphics::Shader *newShader(const Shader::ShaderSource &source) override;
|
||||||
|
|
||||||
love::graphics::Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) override;
|
love::graphics::Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) override;
|
||||||
@@ -84,8 +80,6 @@ public:
|
|||||||
|
|
||||||
love::graphics::Text *newText(love::graphics::Font *font, const std::vector<Font::ColoredString> &text = {}) override;
|
love::graphics::Text *newText(love::graphics::Font *font, const std::vector<Font::ColoredString> &text = {}) override;
|
||||||
|
|
||||||
love::graphics::Video *newVideo(love::video::VideoStream *stream, float pixeldensity) override;
|
|
||||||
|
|
||||||
void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
|
void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
|
||||||
bool setMode(int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil) override;
|
bool setMode(int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil) override;
|
||||||
void unSetMode() override;
|
void unSetMode() override;
|
||||||
@@ -103,7 +97,7 @@ public:
|
|||||||
|
|
||||||
void setColor(Colorf c) override;
|
void setColor(Colorf c) override;
|
||||||
|
|
||||||
void setCanvas(const std::vector<love::graphics::Canvas *> &canvases) override;
|
void setCanvas(const std::vector<RenderTarget> &rts) override;
|
||||||
void setCanvas() override;
|
void setCanvas() override;
|
||||||
|
|
||||||
void setScissor(const Rect &rect) override;
|
void setScissor(const Rect &rect) override;
|
||||||
@@ -149,7 +143,7 @@ private:
|
|||||||
love::graphics::StreamBuffer *newStreamBuffer(BufferType type, size_t size) override;
|
love::graphics::StreamBuffer *newStreamBuffer(BufferType type, size_t size) override;
|
||||||
|
|
||||||
void endPass();
|
void endPass();
|
||||||
void bindCachedFBO(const std::vector<love::graphics::Canvas *> &canvases);
|
void bindCachedFBO(const std::vector<RenderTarget> &targets);
|
||||||
void discard(OpenGL::FramebufferTarget target, const std::vector<bool> &colorbuffers, bool depthstencil);
|
void discard(OpenGL::FramebufferTarget target, const std::vector<bool> &colorbuffers, bool depthstencil);
|
||||||
GLuint attachCachedStencilBuffer(int w, int h, int samples);
|
GLuint attachCachedStencilBuffer(int w, int h, int samples);
|
||||||
|
|
||||||
|
|||||||
@@ -26,16 +26,6 @@
|
|||||||
// STD
|
// STD
|
||||||
#include <algorithm> // for min/max
|
#include <algorithm> // for min/max
|
||||||
|
|
||||||
#ifdef LOVE_ANDROID
|
|
||||||
// log2 is not declared in the math.h shipped with the Android NDK
|
|
||||||
#include <cmath>
|
|
||||||
inline double log2(double n)
|
|
||||||
{
|
|
||||||
// log(n)/log(2) is log2.
|
|
||||||
return std::log(n) / std::log(2);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
namespace graphics
|
namespace graphics
|
||||||
@@ -45,114 +35,38 @@ namespace opengl
|
|||||||
|
|
||||||
float Image::maxMipmapSharpness = 0.0f;
|
float Image::maxMipmapSharpness = 0.0f;
|
||||||
|
|
||||||
static int getMipmapCount(int basewidth, int baseheight)
|
Image::Image(TextureType textype, PixelFormat format, int width, int height, int slices, const Settings &settings)
|
||||||
{
|
: love::graphics::Image(Slices(textype), settings, false)
|
||||||
return (int) log2(std::max(basewidth, baseheight)) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
static bool verifyMipmapLevels(const std::vector<T> &miplevels)
|
|
||||||
{
|
|
||||||
int numlevels = (int) miplevels.size();
|
|
||||||
|
|
||||||
if (numlevels == 1)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
int width = miplevels[0]->getWidth();
|
|
||||||
int height = miplevels[0]->getHeight();
|
|
||||||
|
|
||||||
auto format = miplevels[0]->getFormat();
|
|
||||||
|
|
||||||
int expectedlevels = getMipmapCount(width, height);
|
|
||||||
|
|
||||||
// All mip levels must be present when not using auto-generated mipmaps.
|
|
||||||
if (numlevels != expectedlevels)
|
|
||||||
throw love::Exception("Image does not have all required mipmap levels (expected %d, got %d)", expectedlevels, numlevels);
|
|
||||||
|
|
||||||
// Verify the size of each mip level.
|
|
||||||
for (int i = 1; i < numlevels; i++)
|
|
||||||
{
|
|
||||||
width = std::max(width / 2, 1);
|
|
||||||
height = std::max(height / 2, 1);
|
|
||||||
|
|
||||||
if (miplevels[i]->getWidth() != width)
|
|
||||||
throw love::Exception("Width of image mipmap level %d is incorrect (expected %d, got %d)", i+1, width, miplevels[i]->getWidth());
|
|
||||||
if (miplevels[i]->getHeight() != height)
|
|
||||||
throw love::Exception("Height of image mipmap level %d is incorrect (expected %d, got %d)", i+1, height, miplevels[i]->getHeight());
|
|
||||||
|
|
||||||
if (miplevels[i]->getFormat() != format)
|
|
||||||
throw love::Exception("All image mipmap levels must have the same format.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Image::Image(const std::vector<love::image::ImageData *> &imagedata, const Settings &settings)
|
|
||||||
: love::graphics::Image(settings)
|
|
||||||
, texture(0)
|
, texture(0)
|
||||||
, mipmapSharpness(defaultMipmapSharpness)
|
|
||||||
, compressed(false)
|
, compressed(false)
|
||||||
, sRGB(false)
|
|
||||||
, usingDefaultTexture(false)
|
, usingDefaultTexture(false)
|
||||||
, textureMemorySize(0)
|
, textureMemorySize(0)
|
||||||
{
|
{
|
||||||
if (imagedata.empty())
|
if (isPixelFormatCompressed(format))
|
||||||
throw love::Exception("");
|
throw love::Exception("This constructor is only supported for non-compressed pixel formats.");
|
||||||
|
|
||||||
pixelWidth = imagedata[0]->getWidth();
|
if (textype == TEXTURE_VOLUME)
|
||||||
pixelHeight = imagedata[0]->getHeight();
|
depth = slices;
|
||||||
|
else if (textype == TEXTURE_2D_ARRAY)
|
||||||
|
layers = slices;
|
||||||
|
|
||||||
width = (int) (pixelWidth / settings.pixeldensity + 0.5);
|
init(format, width, height, settings);
|
||||||
height = (int) (pixelHeight / settings.pixeldensity + 0.5);
|
|
||||||
|
|
||||||
if (verifyMipmapLevels(imagedata))
|
|
||||||
this->settings.mipmaps = true;
|
|
||||||
|
|
||||||
for (const auto &id : imagedata)
|
|
||||||
data.push_back(id);
|
|
||||||
|
|
||||||
format = data[0]->getFormat();
|
|
||||||
|
|
||||||
preload();
|
|
||||||
loadVolatile();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::Image(const std::vector<love::image::CompressedImageData *> &compresseddata, const Settings &settings)
|
Image::Image(const Slices &slices, const Settings &settings)
|
||||||
: love::graphics::Image(settings)
|
: love::graphics::Image(slices, settings, true)
|
||||||
, texture(0)
|
, texture(0)
|
||||||
, mipmapSharpness(defaultMipmapSharpness)
|
, compressed(false)
|
||||||
, compressed(true)
|
|
||||||
, sRGB(false)
|
|
||||||
, usingDefaultTexture(false)
|
, usingDefaultTexture(false)
|
||||||
, textureMemorySize(0)
|
, textureMemorySize(0)
|
||||||
{
|
{
|
||||||
pixelWidth = compresseddata[0]->getWidth(0);
|
if (texType == TEXTURE_2D_ARRAY)
|
||||||
pixelHeight = compresseddata[0]->getHeight(0);
|
this->layers = data.getSliceCount();
|
||||||
|
else if (texType == TEXTURE_VOLUME)
|
||||||
|
this->depth = data.getSliceCount();
|
||||||
|
|
||||||
width = (int) (pixelWidth / settings.pixeldensity + 0.5);
|
love::image::ImageDataBase *slice = data.get(0, 0);
|
||||||
height = (int) (pixelHeight / settings.pixeldensity + 0.5);
|
init(slice->getFormat(), slice->getWidth(), slice->getHeight(), settings);
|
||||||
|
|
||||||
if (verifyMipmapLevels(compresseddata))
|
|
||||||
this->settings.mipmaps = true;
|
|
||||||
else if (settings.mipmaps && getMipmapCount(pixelWidth, pixelHeight) != compresseddata[0]->getMipmapCount())
|
|
||||||
{
|
|
||||||
if (compresseddata[0]->getMipmapCount() == 1)
|
|
||||||
this->settings.mipmaps = false;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw love::Exception("Image cannot have mipmaps: compressed image data does not have all required mipmap levels (expected %d, got %d)",
|
|
||||||
getMipmapCount(width, height),
|
|
||||||
compresseddata[0]->getMipmapCount());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (image::CompressedImageData *cd : compresseddata)
|
|
||||||
cdata.push_back(cd);
|
|
||||||
|
|
||||||
format = cdata[0]->getFormat();
|
|
||||||
|
|
||||||
preload();
|
|
||||||
loadVolatile();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::~Image()
|
Image::~Image()
|
||||||
@@ -160,56 +74,41 @@ Image::~Image()
|
|||||||
unloadVolatile();
|
unloadVolatile();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::preload()
|
void Image::init(PixelFormat fmt, int w, int h, const Settings &settings)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 4; i++)
|
pixelWidth = w;
|
||||||
vertices[i].color = Color(255, 255, 255, 255);
|
pixelHeight = h;
|
||||||
|
|
||||||
// Vertices are ordered for use with triangle strips:
|
width = (int) (pixelWidth / settings.pixeldensity + 0.5);
|
||||||
// 0---2
|
height = (int) (pixelHeight / settings.pixeldensity + 0.5);
|
||||||
// | / |
|
|
||||||
// 1---3
|
|
||||||
vertices[0].x = 0.0f;
|
|
||||||
vertices[0].y = 0.0f;
|
|
||||||
vertices[1].x = 0.0f;
|
|
||||||
vertices[1].y = (float) height;
|
|
||||||
vertices[2].x = (float) width;
|
|
||||||
vertices[2].y = 0.0f;
|
|
||||||
vertices[3].x = (float) width;
|
|
||||||
vertices[3].y = (float) height;
|
|
||||||
|
|
||||||
vertices[0].s = 0.0f;
|
mipmapCount = mipmapsType == MIPMAPS_NONE ? 1 : getMipmapCount(w, h);
|
||||||
vertices[0].t = 0.0f;
|
format = fmt;
|
||||||
vertices[1].s = 0.0f;
|
compressed = isPixelFormatCompressed(format);
|
||||||
vertices[1].t = 1.0f;
|
|
||||||
vertices[2].s = 1.0f;
|
|
||||||
vertices[2].t = 0.0f;
|
|
||||||
vertices[3].s = 1.0f;
|
|
||||||
vertices[3].t = 1.0f;
|
|
||||||
|
|
||||||
if (settings.mipmaps)
|
if (compressed && mipmapsType == MIPMAPS_GENERATED)
|
||||||
|
mipmapsType = MIPMAPS_NONE;
|
||||||
|
|
||||||
|
if (getMipmapCount() > 1)
|
||||||
filter.mipmap = defaultMipmapFilter;
|
filter.mipmap = defaultMipmapFilter;
|
||||||
|
|
||||||
if (!isGammaCorrect())
|
loadVolatile();
|
||||||
settings.linear = false;
|
initVertices();
|
||||||
|
|
||||||
if (isGammaCorrect() && !settings.linear)
|
|
||||||
sRGB = true;
|
|
||||||
else
|
|
||||||
sRGB = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::generateMipmaps()
|
void Image::generateMipmaps()
|
||||||
{
|
{
|
||||||
// The GL_GENERATE_MIPMAP texparameter is set in loadVolatile if we don't
|
// The GL_GENERATE_MIPMAP texparameter is set in loadVolatile if we don't
|
||||||
// have support for glGenerateMipmap.
|
// have support for glGenerateMipmap.
|
||||||
if (settings.mipmaps && !isCompressed() &&
|
if (getMipmapCount() > 1 && !isCompressed() &&
|
||||||
(GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
|
(GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_EXT_framebuffer_object))
|
||||||
{
|
{
|
||||||
if (gl.bugs.generateMipmapsRequiresTexture2DEnable)
|
GLenum gltextype = OpenGL::getGLTextureType(texType);
|
||||||
glEnable(GL_TEXTURE_2D);
|
|
||||||
|
|
||||||
glGenerateMipmap(GL_TEXTURE_2D);
|
if (gl.bugs.generateMipmapsRequiresTexture2DEnable)
|
||||||
|
glEnable(gltextype);
|
||||||
|
|
||||||
|
glGenerateMipmap(gltextype);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,65 +116,120 @@ void Image::loadDefaultTexture()
|
|||||||
{
|
{
|
||||||
usingDefaultTexture = true;
|
usingDefaultTexture = true;
|
||||||
|
|
||||||
gl.bindTextureToUnit(texture, 0, false);
|
gl.bindTextureToUnit(this, 0, false);
|
||||||
setFilter(filter);
|
setFilter(filter);
|
||||||
|
|
||||||
|
bool isSRGB = false;
|
||||||
|
gl.rawTexStorage(texType, 1, PIXELFORMAT_RGBA8, isSRGB, 2, 2, 1);
|
||||||
|
|
||||||
// A nice friendly checkerboard to signify invalid textures...
|
// A nice friendly checkerboard to signify invalid textures...
|
||||||
GLubyte px[] = {0xFF,0xFF,0xFF,0xFF, 0xFF,0xA0,0xA0,0xFF,
|
GLubyte px[] = {0xFF,0xFF,0xFF,0xFF, 0xFF,0xA0,0xA0,0xFF,
|
||||||
0xFF,0xA0,0xA0,0xFF, 0xFF,0xFF,0xFF,0xFF};
|
0xFF,0xA0,0xA0,0xFF, 0xFF,0xFF,0xFF,0xFF};
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, px);
|
int slices = texType == TEXTURE_CUBE ? 6 : 1;
|
||||||
|
Rect rect = {0, 0, 2, 2};
|
||||||
|
for (int slice = 0; slice < slices; slice++)
|
||||||
|
uploadByteData(PIXELFORMAT_RGBA8, px, sizeof(px), rect, 0, slice);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::loadFromCompressedData()
|
void Image::loadData()
|
||||||
{
|
{
|
||||||
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, false, sRGB);
|
int mipcount = getMipmapCount();
|
||||||
|
int slicecount = 1;
|
||||||
|
|
||||||
if (isGammaCorrect() && !sRGB)
|
if (texType == TEXTURE_VOLUME)
|
||||||
settings.linear = true;
|
slicecount = getDepth();
|
||||||
|
else if (texType == TEXTURE_2D_ARRAY)
|
||||||
|
slicecount = getLayerCount();
|
||||||
|
else if (texType == TEXTURE_CUBE)
|
||||||
|
slicecount = 6;
|
||||||
|
|
||||||
int count = 1;
|
if (!isCompressed())
|
||||||
|
gl.rawTexStorage(texType, mipcount, format, sRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers);
|
||||||
|
|
||||||
if (settings.mipmaps && cdata.size() > 1)
|
if (mipmapsType == MIPMAPS_GENERATED)
|
||||||
count = (int) cdata.size();
|
mipcount = 1;
|
||||||
else if (settings.mipmaps)
|
|
||||||
count = cdata[0]->getMipmapCount();
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
int w = pixelWidth;
|
||||||
|
int h = pixelHeight;
|
||||||
|
int d = depth;
|
||||||
|
|
||||||
|
OpenGL::TextureFormat fmt = gl.convertPixelFormat(format, false, sRGB);
|
||||||
|
|
||||||
|
for (int mip = 0; mip < mipcount; mip++)
|
||||||
{
|
{
|
||||||
// Compressed image mipmaps can come from separate CompressedImageData
|
if (isCompressed() && (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME))
|
||||||
// objects, or all from a single object.
|
|
||||||
auto cd = cdata.size() > 1 ? cdata[i].get() : cdata[0].get();
|
|
||||||
int datamip = cdata.size() > 1 ? 0 : i;
|
|
||||||
|
|
||||||
glCompressedTexImage2D(GL_TEXTURE_2D, i, fmt.internalformat,
|
|
||||||
cd->getWidth(datamip), cd->getHeight(datamip), 0,
|
|
||||||
(GLsizei) cd->getSize(datamip), cd->getData(datamip));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Image::loadFromImageData()
|
|
||||||
{
|
|
||||||
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, false, sRGB);
|
|
||||||
|
|
||||||
if (isGammaCorrect() && !sRGB)
|
|
||||||
settings.linear = true;
|
|
||||||
|
|
||||||
int mipcount = settings.mipmaps ? (int) data.size() : 1;
|
|
||||||
|
|
||||||
for (int i = 0; i < mipcount; i++)
|
|
||||||
{
|
{
|
||||||
love::image::ImageData *id = data[i].get();
|
size_t mipsize = 0;
|
||||||
love::thread::Lock lock(id->getMutex());
|
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, i, fmt.internalformat, id->getWidth(), id->getHeight(),
|
if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME)
|
||||||
0, fmt.externalformat, fmt.type, id->getData());
|
{
|
||||||
|
for (int slice = 0; slice < data.getSliceCount(mip); slice++)
|
||||||
|
mipsize += data.get(slice, mip)->getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.size() <= 1)
|
GLenum gltarget = OpenGL::getGLTextureType(texType);
|
||||||
|
glCompressedTexImage3D(gltarget, mip, fmt.internalformat, w, h, d, 0, mipsize, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int slice = 0; slice < slicecount; slice++)
|
||||||
|
{
|
||||||
|
love::image::ImageDataBase *id = data.get(slice, mip);
|
||||||
|
|
||||||
|
if (id != nullptr)
|
||||||
|
uploadImageData(id, mip, slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
w = std::max(w / 2, 1);
|
||||||
|
h = std::max(h / 2, 1);
|
||||||
|
|
||||||
|
if (texType == TEXTURE_VOLUME)
|
||||||
|
d = std::max(d / 2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mipmapsType == MIPMAPS_GENERATED)
|
||||||
generateMipmaps();
|
generateMipmaps();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Image::uploadByteData(PixelFormat pixelformat, const void *data, size_t size, const Rect &r, int level, int slice)
|
||||||
|
{
|
||||||
|
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(pixelformat, false, sRGB);
|
||||||
|
GLenum gltarget = OpenGL::getGLTextureType(texType);
|
||||||
|
|
||||||
|
if (texType == TEXTURE_CUBE)
|
||||||
|
gltarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
|
||||||
|
|
||||||
|
if (isPixelFormatCompressed(pixelformat))
|
||||||
|
{
|
||||||
|
if (r.x != 0 || r.y != 0)
|
||||||
|
throw love::Exception("x and y parameters must be 0 for compressed images.");
|
||||||
|
|
||||||
|
if (texType == TEXTURE_2D || texType == TEXTURE_CUBE)
|
||||||
|
glCompressedTexImage2D(gltarget, level, fmt.internalformat, r.w, r.h, 0, size, data);
|
||||||
|
else if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME)
|
||||||
|
glCompressedTexSubImage3D(gltarget, level, 0, 0, slice, r.w, r.h, 1, fmt.internalformat, size, data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (texType == TEXTURE_2D || texType == TEXTURE_CUBE)
|
||||||
|
glTexSubImage2D(gltarget, level, r.x, r.y, r.w, r.h, fmt.externalformat, fmt.type, data);
|
||||||
|
else if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME)
|
||||||
|
glTexSubImage3D(gltarget, level, r.x, r.y, slice, r.w, r.h, 1, fmt.externalformat, fmt.type, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::uploadImageData(love::image::ImageDataBase *d, int level, int slice)
|
||||||
|
{
|
||||||
|
love::image::ImageData *id = dynamic_cast<love::image::ImageData *>(d);
|
||||||
|
|
||||||
|
love::thread::EmptyLock lock;
|
||||||
|
if (id != nullptr)
|
||||||
|
lock.setLock(id->getMutex());
|
||||||
|
|
||||||
|
Rect rect = {0, 0, d->getWidth(), d->getHeight()};
|
||||||
|
uploadByteData(d->getFormat(), d->getData(), d->getSize(), rect, level, slice);
|
||||||
|
}
|
||||||
|
|
||||||
bool Image::loadVolatile()
|
bool Image::loadVolatile()
|
||||||
{
|
{
|
||||||
if (texture != 0)
|
if (texture != 0)
|
||||||
@@ -301,9 +255,9 @@ bool Image::loadVolatile()
|
|||||||
|
|
||||||
// GL_EXT_sRGB doesn't support glGenerateMipmap for sRGB textures.
|
// GL_EXT_sRGB doesn't support glGenerateMipmap for sRGB textures.
|
||||||
if (sRGB && (GLAD_ES_VERSION_2_0 && GLAD_EXT_sRGB && !GLAD_ES_VERSION_3_0)
|
if (sRGB && (GLAD_ES_VERSION_2_0 && GLAD_EXT_sRGB && !GLAD_ES_VERSION_3_0)
|
||||||
&& data.size() <= 1)
|
&& mipmapsType != MIPMAPS_DATA)
|
||||||
{
|
{
|
||||||
settings.mipmaps = false;
|
mipmapsType = MIPMAPS_NONE;
|
||||||
filter.mipmap = FILTER_NONE;
|
filter.mipmap = FILTER_NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -312,7 +266,7 @@ bool Image::loadVolatile()
|
|||||||
if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
|
if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
|
||||||
&& (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight)))
|
&& (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight)))
|
||||||
{
|
{
|
||||||
settings.mipmaps = false;
|
mipmapsType = MIPMAPS_NONE;
|
||||||
filter.mipmap = FILTER_NONE;
|
filter.mipmap = FILTER_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,38 +274,43 @@ bool Image::loadVolatile()
|
|||||||
glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxMipmapSharpness);
|
glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxMipmapSharpness);
|
||||||
|
|
||||||
glGenTextures(1, &texture);
|
glGenTextures(1, &texture);
|
||||||
gl.bindTextureToUnit(texture, 0, false);
|
gl.bindTextureToUnit(this, 0, false);
|
||||||
|
|
||||||
setFilter(filter);
|
bool loaddefault = false;
|
||||||
setWrap(wrap);
|
|
||||||
setMipmapSharpness(mipmapSharpness);
|
int max2Dsize = gl.getMax2DTextureSize();
|
||||||
|
int max3Dsize = gl.getMax3DTextureSize();
|
||||||
|
|
||||||
|
if ((texType == TEXTURE_2D || texType == TEXTURE_2D_ARRAY) && (pixelWidth > max2Dsize || pixelHeight > max2Dsize))
|
||||||
|
loaddefault = true;
|
||||||
|
else if (texType == TEXTURE_2D_ARRAY && layers > gl.getMaxTextureLayers())
|
||||||
|
loaddefault = true;
|
||||||
|
else if (texType == TEXTURE_CUBE && (pixelWidth > gl.getMaxCubeTextureSize() || pixelWidth != pixelHeight))
|
||||||
|
loaddefault = true;
|
||||||
|
else if (texType == TEXTURE_VOLUME && (pixelWidth > max3Dsize || pixelHeight > max3Dsize || depth > max3Dsize))
|
||||||
|
loaddefault = true;
|
||||||
|
|
||||||
// Use a default texture if the size is too big for the system.
|
// Use a default texture if the size is too big for the system.
|
||||||
if (pixelWidth > gl.getMaxTextureSize() || pixelHeight > gl.getMaxTextureSize())
|
if (loaddefault)
|
||||||
{
|
{
|
||||||
loadDefaultTexture();
|
loadDefaultTexture();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!settings.mipmaps && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_1_0))
|
setFilter(filter);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
setWrap(wrap);
|
||||||
|
setMipmapSharpness(mipmapSharpness);
|
||||||
|
|
||||||
if (settings.mipmaps && !isCompressed() && data.size() <= 1 &&
|
GLenum gltextype = OpenGL::getGLTextureType(texType);
|
||||||
!(GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
|
|
||||||
{
|
if (mipmapsType == MIPMAPS_NONE && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_1_0))
|
||||||
// Auto-generate mipmaps every time the texture is modified, if
|
glTexParameteri(gltextype, GL_TEXTURE_MAX_LEVEL, 0);
|
||||||
// glGenerateMipmap isn't supported.
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (glGetError() != GL_NO_ERROR); // Clear errors.
|
while (glGetError() != GL_NO_ERROR); // Clear errors.
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (isCompressed())
|
loadData();
|
||||||
loadFromCompressedData();
|
|
||||||
else
|
|
||||||
loadFromImageData();
|
|
||||||
|
|
||||||
GLenum glerr = glGetError();
|
GLenum glerr = glGetError();
|
||||||
if (glerr != GL_NO_ERROR)
|
if (glerr != GL_NO_ERROR)
|
||||||
@@ -365,13 +324,12 @@ bool Image::loadVolatile()
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t prevmemsize = textureMemorySize;
|
size_t prevmemsize = textureMemorySize;
|
||||||
|
textureMemorySize = 0;
|
||||||
|
|
||||||
if (isCompressed())
|
for (int slice = 0; slice < data.getSliceCount(0); slice++)
|
||||||
textureMemorySize = cdata[0]->getSize();
|
textureMemorySize += data.get(slice, 0)->getSize();
|
||||||
else
|
|
||||||
textureMemorySize = data[0]->getSize();
|
|
||||||
|
|
||||||
if (settings.mipmaps)
|
if (getMipmapCount() > 1)
|
||||||
textureMemorySize *= 1.33334;
|
textureMemorySize *= 1.33334;
|
||||||
|
|
||||||
gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
|
gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
|
||||||
@@ -392,53 +350,61 @@ void Image::unloadVolatile()
|
|||||||
textureMemorySize = 0;
|
textureMemorySize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Image::refresh(int xoffset, int yoffset, int w, int h)
|
void Image::replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, bool reloadmipmaps)
|
||||||
{
|
{
|
||||||
// No effect if the texture hasn't been created yet.
|
// No effect if the texture hasn't been created yet.
|
||||||
if (texture == 0 || usingDefaultTexture)
|
if (texture == 0 || usingDefaultTexture)
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
if (xoffset < 0 || yoffset < 0 || w <= 0 || h <= 0 ||
|
if (d->getFormat() != getPixelFormat())
|
||||||
(xoffset + w) > pixelWidth || (yoffset + h) > pixelHeight)
|
throw love::Exception("Pixel formats must match.");
|
||||||
|
|
||||||
|
if (mipmap < 0 || (mipmapsType != MIPMAPS_DATA && mipmap > 0) || mipmap >= getMipmapCount())
|
||||||
|
throw love::Exception("Invalid image mipmap index.");
|
||||||
|
|
||||||
|
if (slice < 0 || (texType == TEXTURE_CUBE && slice >= 6)
|
||||||
|
|| (texType == TEXTURE_VOLUME && slice >= std::max(getDepth() >> mipmap, 1))
|
||||||
|
|| (texType == TEXTURE_2D_ARRAY && slice >= getLayerCount()))
|
||||||
{
|
{
|
||||||
throw love::Exception("Invalid rectangle dimensions.");
|
throw love::Exception("Invalid image slice index.");
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenGL::TempDebugGroup debuggroup("Image refresh");
|
love::image::ImageDataBase *oldd = data.get(slice, mipmap);
|
||||||
|
|
||||||
gl.bindTextureToUnit(texture, 0, false);
|
if (oldd == nullptr)
|
||||||
|
throw love::Exception("Image does not store ImageData!");
|
||||||
|
|
||||||
if (isCompressed())
|
int w = d->getWidth();
|
||||||
{
|
int h = d->getHeight();
|
||||||
loadFromCompressedData();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isSRGB = sRGB;
|
if (w != oldd->getWidth() || h != oldd->getHeight())
|
||||||
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, false, isSRGB);
|
throw love::Exception("Dimensions must match the texture's dimensions for the specified mipmap level.");
|
||||||
|
|
||||||
int mipcount = settings.mipmaps ? (int) data.size() : 1;
|
d->retain();
|
||||||
|
oldd->release();
|
||||||
|
|
||||||
// Reupload the sub-rectangle of each mip level (if we have custom mipmaps.)
|
data.set(slice, mipmap, d);
|
||||||
for (int i = 0; i < mipcount; i++)
|
|
||||||
{
|
|
||||||
const image::pixel *pdata = (const image::pixel *) data[i]->getData();
|
|
||||||
pdata += yoffset * data[i]->getWidth() + xoffset;
|
|
||||||
|
|
||||||
thread::Lock lock(data[i]->getMutex());
|
OpenGL::TempDebugGroup debuggroup("Image replace pixels");
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, i, xoffset, yoffset, w, h,
|
|
||||||
fmt.externalformat, fmt.type, pdata);
|
|
||||||
|
|
||||||
xoffset /= 2;
|
gl.bindTextureToUnit(this, 0, false);
|
||||||
yoffset /= 2;
|
|
||||||
w = std::max(w / 2, 1);
|
|
||||||
h = std::max(h / 2, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.size() <= 1)
|
uploadImageData(d, mipmap, slice);
|
||||||
|
|
||||||
|
if (reloadmipmaps && mipmap == 0 && getMipmapCount() > 1)
|
||||||
generateMipmaps();
|
generateMipmaps();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
void Image::replacePixels(const void *data, size_t size, const Rect &rect, int slice, int mipmap, bool reloadmipmaps)
|
||||||
|
{
|
||||||
|
OpenGL::TempDebugGroup debuggroup("Image replace pixels");
|
||||||
|
|
||||||
|
gl.bindTextureToUnit(this, 0, false);
|
||||||
|
|
||||||
|
uploadByteData(format, data, size, rect, mipmap, slice);
|
||||||
|
|
||||||
|
if (reloadmipmaps && mipmap == 0 && getMipmapCount() > 1)
|
||||||
|
generateMipmaps();
|
||||||
}
|
}
|
||||||
|
|
||||||
ptrdiff_t Image::getHandle() const
|
ptrdiff_t Image::getHandle() const
|
||||||
@@ -446,21 +412,11 @@ ptrdiff_t Image::getHandle() const
|
|||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<StrongRef<love::image::ImageData>> &Image::getImageData() const
|
|
||||||
{
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<StrongRef<love::image::CompressedImageData>> &Image::getCompressedData() const
|
|
||||||
{
|
|
||||||
return cdata;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Image::setFilter(const Texture::Filter &f)
|
void Image::setFilter(const Texture::Filter &f)
|
||||||
{
|
{
|
||||||
if (!validateFilter(f, settings.mipmaps))
|
if (!validateFilter(f, getMipmapCount() > 1))
|
||||||
{
|
{
|
||||||
if (f.mipmap != FILTER_NONE && !settings.mipmaps)
|
if (f.mipmap != FILTER_NONE && getMipmapCount() == 1)
|
||||||
throw love::Exception("Non-mipmapped image cannot have mipmap filtering.");
|
throw love::Exception("Non-mipmapped image cannot have mipmap filtering.");
|
||||||
else
|
else
|
||||||
throw love::Exception("Invalid texture filter.");
|
throw love::Exception("Invalid texture filter.");
|
||||||
@@ -468,7 +424,7 @@ void Image::setFilter(const Texture::Filter &f)
|
|||||||
|
|
||||||
filter = f;
|
filter = f;
|
||||||
|
|
||||||
if (!data.empty() && !OpenGL::hasTextureFilteringSupport(data[0]->getFormat()))
|
if (!OpenGL::hasTextureFilteringSupport(getPixelFormat()))
|
||||||
{
|
{
|
||||||
filter.mag = filter.min = FILTER_NEAREST;
|
filter.mag = filter.min = FILTER_NEAREST;
|
||||||
|
|
||||||
@@ -483,57 +439,64 @@ void Image::setFilter(const Texture::Filter &f)
|
|||||||
filter.min = filter.mag = FILTER_NEAREST;
|
filter.min = filter.mag = FILTER_NEAREST;
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.bindTextureToUnit(texture, 0, false);
|
gl.bindTextureToUnit(this, 0, false);
|
||||||
gl.setTextureFilter(filter);
|
gl.setTextureFilter(texType, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Image::setWrap(const Texture::Wrap &w)
|
bool Image::setWrap(const Texture::Wrap &w)
|
||||||
{
|
{
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
bool forceclamp = texType == TEXTURE_CUBE;
|
||||||
wrap = w;
|
wrap = w;
|
||||||
|
|
||||||
|
// If we only have limited NPOT support then the wrap mode must be CLAMP.
|
||||||
if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
|
if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
|
||||||
&& (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight)))
|
&& (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight) || depth != nextP2(depth)))
|
||||||
{
|
{
|
||||||
if (wrap.s != WRAP_CLAMP || wrap.t != WRAP_CLAMP)
|
forceclamp = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forceclamp)
|
||||||
|
{
|
||||||
|
if (wrap.s != WRAP_CLAMP || wrap.t != WRAP_CLAMP || wrap.r != WRAP_CLAMP)
|
||||||
success = false;
|
success = false;
|
||||||
|
|
||||||
// If we only have limited NPOT support then the wrap mode must be CLAMP.
|
wrap.s = wrap.t = wrap.r = WRAP_CLAMP;
|
||||||
wrap.s = wrap.t = WRAP_CLAMP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gl.isClampZeroTextureWrapSupported())
|
if (!gl.isClampZeroTextureWrapSupported())
|
||||||
{
|
{
|
||||||
if (wrap.s == WRAP_CLAMP_ZERO)
|
if (wrap.s == WRAP_CLAMP_ZERO) wrap.s = WRAP_CLAMP;
|
||||||
wrap.s = WRAP_CLAMP;
|
if (wrap.t == WRAP_CLAMP_ZERO) wrap.t = WRAP_CLAMP;
|
||||||
if (wrap.t == WRAP_CLAMP_ZERO)
|
if (wrap.r == WRAP_CLAMP_ZERO) wrap.r = WRAP_CLAMP;
|
||||||
wrap.t = WRAP_CLAMP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.bindTextureToUnit(texture, 0, false);
|
gl.bindTextureToUnit(this, 0, false);
|
||||||
gl.setTextureWrap(wrap);
|
gl.setTextureWrap(texType, wrap);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::setMipmapSharpness(float sharpness)
|
bool Image::setMipmapSharpness(float sharpness)
|
||||||
{
|
{
|
||||||
// OpenGL ES doesn't support LOD bias via glTexParameter.
|
// OpenGL ES doesn't support LOD bias via glTexParameter.
|
||||||
if (!GLAD_VERSION_1_4)
|
if (!GLAD_VERSION_1_4)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
// LOD bias has the range (-maxbias, maxbias)
|
// LOD bias has the range (-maxbias, maxbias)
|
||||||
mipmapSharpness = std::min(std::max(sharpness, -maxMipmapSharpness + 0.01f), maxMipmapSharpness - 0.01f);
|
mipmapSharpness = std::min(std::max(sharpness, -maxMipmapSharpness + 0.01f), maxMipmapSharpness - 0.01f);
|
||||||
|
|
||||||
gl.bindTextureToUnit(texture, 0, false);
|
gl.bindTextureToUnit(this, 0, false);
|
||||||
|
|
||||||
// negative bias is sharper
|
// negative bias is sharper
|
||||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapSharpness);
|
GLenum gltextype = OpenGL::getGLTextureType(texType);
|
||||||
|
glTexParameterf(gltextype, GL_TEXTURE_LOD_BIAS, -mipmapSharpness);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Image::getMipmapSharpness() const
|
bool Image::isFormatLinear() const
|
||||||
{
|
{
|
||||||
return mipmapSharpness;
|
return isGammaCorrect() && !sRGB;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Image::isCompressed() const
|
bool Image::isCompressed() const
|
||||||
@@ -541,6 +504,11 @@ bool Image::isCompressed() const
|
|||||||
return compressed;
|
return compressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Image::MipmapsType Image::getMipmapsType() const
|
||||||
|
{
|
||||||
|
return mipmapsType;
|
||||||
|
}
|
||||||
|
|
||||||
bool Image::isFormatSupported(PixelFormat pixelformat)
|
bool Image::isFormatSupported(PixelFormat pixelformat)
|
||||||
{
|
{
|
||||||
return OpenGL::isPixelFormatSupported(pixelformat, false, false);
|
return OpenGL::isPixelFormatSupported(pixelformat, false, false);
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ class Image final : public love::graphics::Image, public Volatile
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Image(const std::vector<love::image::ImageData *> &data, const Settings &settings);
|
Image(const Slices &data, const Settings &settings);
|
||||||
Image(const std::vector<love::image::CompressedImageData *> &cdata, const Settings &settings);
|
Image(TextureType textype, PixelFormat format, int width, int height, int slices, const Settings &settings);
|
||||||
|
|
||||||
virtual ~Image();
|
virtual ~Image();
|
||||||
|
|
||||||
@@ -49,52 +49,37 @@ public:
|
|||||||
|
|
||||||
ptrdiff_t getHandle() const override;
|
ptrdiff_t getHandle() const override;
|
||||||
|
|
||||||
const std::vector<StrongRef<love::image::ImageData>> &getImageData() const override;
|
|
||||||
const std::vector<StrongRef<love::image::CompressedImageData>> &getCompressedData() const override;
|
|
||||||
|
|
||||||
void setFilter(const Texture::Filter &f) override;
|
void setFilter(const Texture::Filter &f) override;
|
||||||
bool setWrap(const Texture::Wrap &w) override;
|
bool setWrap(const Texture::Wrap &w) override;
|
||||||
|
|
||||||
void setMipmapSharpness(float sharpness) override;
|
bool setMipmapSharpness(float sharpness) override;
|
||||||
float getMipmapSharpness() const override;
|
|
||||||
|
void replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, bool reloadmipmaps) override;
|
||||||
|
void replacePixels(const void *data, size_t size, const Rect &rect, int slice, int mipmap, bool reloadmipmaps) override;
|
||||||
|
|
||||||
|
bool isFormatLinear() const override;
|
||||||
bool isCompressed() const override;
|
bool isCompressed() const override;
|
||||||
bool refresh(int xoffset, int yoffset, int w, int h) override;
|
MipmapsType getMipmapsType() const override;
|
||||||
|
|
||||||
static bool isFormatSupported(PixelFormat pixelformat);
|
static bool isFormatSupported(PixelFormat pixelformat);
|
||||||
static bool hasSRGBSupport();
|
static bool hasSRGBSupport();
|
||||||
|
|
||||||
static bool getConstant(const char *in, SettingType &out);
|
|
||||||
static bool getConstant(SettingType in, const char *&out);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void preload();
|
void init(PixelFormat fmt, int w, int h, const Settings &settings);
|
||||||
|
|
||||||
void generateMipmaps();
|
void generateMipmaps();
|
||||||
void loadDefaultTexture();
|
void loadDefaultTexture();
|
||||||
void loadFromCompressedData();
|
void loadData();
|
||||||
void loadFromImageData();
|
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, const Rect &rect, int level, int slice);
|
||||||
|
void uploadImageData(love::image::ImageDataBase *d, int level, int slice);
|
||||||
// The ImageData from which the texture is created. May be empty if
|
|
||||||
// Compressed image data was used to create the texture.
|
|
||||||
// Each element in the array is a mipmap level.
|
|
||||||
std::vector<StrongRef<love::image::ImageData>> data;
|
|
||||||
|
|
||||||
// Or the Compressed Image Data from which the texture is created. May be
|
|
||||||
// empty if raw ImageData was used to create the texture.
|
|
||||||
std::vector<StrongRef<love::image::CompressedImageData>> cdata;
|
|
||||||
|
|
||||||
// OpenGL texture identifier.
|
// OpenGL texture identifier.
|
||||||
GLuint texture;
|
GLuint texture;
|
||||||
|
|
||||||
// Mipmap texture LOD bias (sharpness) value.
|
|
||||||
float mipmapSharpness;
|
|
||||||
|
|
||||||
// Whether this Image is using a compressed texture.
|
// Whether this Image is using a compressed texture.
|
||||||
bool compressed;
|
bool compressed;
|
||||||
|
|
||||||
bool sRGB;
|
|
||||||
|
|
||||||
// True if the image wasn't able to be properly created and it had to fall
|
// True if the image wasn't able to be properly created and it had to fall
|
||||||
// back to a default texture.
|
// back to a default texture.
|
||||||
bool usingDefaultTexture;
|
bool usingDefaultTexture;
|
||||||
|
|||||||
@@ -99,6 +99,9 @@ void Mesh::drawInstanced(love::graphics::Graphics *gfx, const love::Matrix4 &m,
|
|||||||
if (instancecount > 1 && !gl.isInstancingSupported())
|
if (instancecount > 1 && !gl.isInstancingSupported())
|
||||||
throw love::Exception("Instancing is not supported on this system.");
|
throw love::Exception("Instancing is not supported on this system.");
|
||||||
|
|
||||||
|
if (Shader::current && texture.get())
|
||||||
|
Shader::current->checkMainTextureType(texture->getTextureType());
|
||||||
|
|
||||||
gfx->flushStreamDraws();
|
gfx->flushStreamDraws();
|
||||||
|
|
||||||
OpenGL::TempDebugGroup debuggroup("Mesh draw");
|
OpenGL::TempDebugGroup debuggroup("Mesh draw");
|
||||||
@@ -131,7 +134,10 @@ void Mesh::drawInstanced(love::graphics::Graphics *gfx, const love::Matrix4 &m,
|
|||||||
|
|
||||||
gl.useVertexAttribArrays(enabledattribs, instancedattribs);
|
gl.useVertexAttribArrays(enabledattribs, instancedattribs);
|
||||||
|
|
||||||
|
if (texture.get())
|
||||||
gl.bindTextureToUnit(texture, 0, false);
|
gl.bindTextureToUnit(texture, 0, false);
|
||||||
|
else
|
||||||
|
gl.bindTextureToUnit(TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D), 0, false);
|
||||||
|
|
||||||
Graphics::TempTransform transform(gfx, m);
|
Graphics::TempTransform transform(gfx, m);
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,10 @@ OpenGL::OpenGL()
|
|||||||
, contextInitialized(false)
|
, contextInitialized(false)
|
||||||
, pixelShaderHighpSupported(false)
|
, pixelShaderHighpSupported(false)
|
||||||
, maxAnisotropy(1.0f)
|
, maxAnisotropy(1.0f)
|
||||||
, maxTextureSize(0)
|
, max2DTextureSize(0)
|
||||||
|
, max3DTextureSize(0)
|
||||||
|
, maxCubeTextureSize(0)
|
||||||
|
, maxTextureArrayLayers(0)
|
||||||
, maxRenderTargets(1)
|
, maxRenderTargets(1)
|
||||||
, maxRenderbufferSamples(0)
|
, maxRenderbufferSamples(0)
|
||||||
, maxTextureUnits(1)
|
, maxTextureUnits(1)
|
||||||
@@ -202,13 +205,18 @@ void OpenGL::setupContext()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize multiple texture unit support for shaders.
|
// Initialize multiple texture unit support for shaders.
|
||||||
state.boundTextures.clear();
|
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
|
||||||
state.boundTextures.resize(maxTextureUnits, 0);
|
{
|
||||||
|
state.boundTextures[i].clear();
|
||||||
|
state.boundTextures[i].resize(maxTextureUnits, 0);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < (int) state.boundTextures.size(); i++)
|
for (int i = 0; i < maxTextureUnits; i++)
|
||||||
{
|
{
|
||||||
glActiveTexture(GL_TEXTURE0 + i);
|
glActiveTexture(GL_TEXTURE0 + i);
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
|
for (int j = 0; j < TEXTURE_MAX_ENUM; j++)
|
||||||
|
glBindTexture(getGLTextureType((TextureType) j), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
@@ -224,8 +232,14 @@ void OpenGL::deInitContext()
|
|||||||
if (!contextInitialized)
|
if (!contextInitialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
glDeleteTextures(1, &state.defaultTexture);
|
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
|
||||||
state.defaultTexture = 0;
|
{
|
||||||
|
if (state.defaultTexture[i] != 0)
|
||||||
|
{
|
||||||
|
gl.deleteTexture(state.defaultTexture[i]);
|
||||||
|
state.defaultTexture[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
contextInitialized = false;
|
contextInitialized = false;
|
||||||
}
|
}
|
||||||
@@ -285,11 +299,15 @@ void OpenGL::initOpenGLFunctions()
|
|||||||
fp_glGenFramebuffers = fp_glGenFramebuffersEXT;
|
fp_glGenFramebuffers = fp_glGenFramebuffersEXT;
|
||||||
fp_glCheckFramebufferStatus = fp_glCheckFramebufferStatusEXT;
|
fp_glCheckFramebufferStatus = fp_glCheckFramebufferStatusEXT;
|
||||||
fp_glFramebufferTexture2D = fp_glFramebufferTexture2DEXT;
|
fp_glFramebufferTexture2D = fp_glFramebufferTexture2DEXT;
|
||||||
|
fp_glFramebufferTexture3D = fp_glFramebufferTexture3DEXT;
|
||||||
fp_glFramebufferRenderbuffer = fp_glFramebufferRenderbufferEXT;
|
fp_glFramebufferRenderbuffer = fp_glFramebufferRenderbufferEXT;
|
||||||
fp_glGetFramebufferAttachmentParameteriv = fp_glGetFramebufferAttachmentParameterivEXT;
|
fp_glGetFramebufferAttachmentParameteriv = fp_glGetFramebufferAttachmentParameterivEXT;
|
||||||
fp_glGenerateMipmap = fp_glGenerateMipmapEXT;
|
fp_glGenerateMipmap = fp_glGenerateMipmapEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GLAD_VERSION_1_0 && GLAD_EXT_texture_array)
|
||||||
|
fp_glFramebufferTextureLayer = fp_glFramebufferTextureLayerEXT;
|
||||||
|
|
||||||
if (GLAD_EXT_framebuffer_blit)
|
if (GLAD_EXT_framebuffer_blit)
|
||||||
fp_glBlitFramebuffer = fp_glBlitFramebufferEXT;
|
fp_glBlitFramebuffer = fp_glBlitFramebufferEXT;
|
||||||
else if (GLAD_ANGLE_framebuffer_blit)
|
else if (GLAD_ANGLE_framebuffer_blit)
|
||||||
@@ -328,6 +346,17 @@ void OpenGL::initOpenGLFunctions()
|
|||||||
fp_glVertexAttribDivisor = fp_glVertexAttribDivisorANGLE;
|
fp_glVertexAttribDivisor = fp_glVertexAttribDivisorANGLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GLAD_ES_VERSION_2_0 && GLAD_OES_texture_3D && !GLAD_ES_VERSION_3_0)
|
||||||
|
{
|
||||||
|
// Function signatures don't match, we'll have to conditionally call it
|
||||||
|
//fp_glTexImage3D = fp_glTexImage3DOES;
|
||||||
|
fp_glTexSubImage3D = fp_glTexSubImage3DOES;
|
||||||
|
fp_glCopyTexSubImage3D = fp_glCopyTexSubImage3DOES;
|
||||||
|
fp_glCompressedTexImage3D = fp_glCompressedTexImage3DOES;
|
||||||
|
fp_glCompressedTexSubImage3D = fp_glCompressedTexSubImage3DOES;
|
||||||
|
fp_glFramebufferTexture3D = fp_glFramebufferTexture3DOES;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGL::initMaxValues()
|
void OpenGL::initMaxValues()
|
||||||
@@ -348,7 +377,18 @@ void OpenGL::initMaxValues()
|
|||||||
else
|
else
|
||||||
maxAnisotropy = 1.0f;
|
maxAnisotropy = 1.0f;
|
||||||
|
|
||||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max2DTextureSize);
|
||||||
|
glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &maxCubeTextureSize);
|
||||||
|
|
||||||
|
if (isTextureTypeSupported(TEXTURE_VOLUME))
|
||||||
|
glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &max3DTextureSize);
|
||||||
|
else
|
||||||
|
max3DTextureSize = 0;
|
||||||
|
|
||||||
|
if (isTextureTypeSupported(TEXTURE_2D_ARRAY))
|
||||||
|
glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxTextureArrayLayers);
|
||||||
|
else
|
||||||
|
maxTextureArrayLayers = 0;
|
||||||
|
|
||||||
int maxattachments = 1;
|
int maxattachments = 1;
|
||||||
int maxdrawbuffers = 1;
|
int maxdrawbuffers = 1;
|
||||||
@@ -382,26 +422,57 @@ void OpenGL::initMaxValues()
|
|||||||
|
|
||||||
void OpenGL::createDefaultTexture()
|
void OpenGL::createDefaultTexture()
|
||||||
{
|
{
|
||||||
// Set the 'default' texture (id 0) as a repeating white pixel. Otherwise,
|
// Set the 'default' texture as a repeating white pixel. Otherwise, texture
|
||||||
// texture2D calls inside a shader would return black when drawing graphics
|
// calls inside a shader would return black when drawing graphics primitives
|
||||||
// primitives, which would create the need to use different "passthrough"
|
// which would create the need to use different "passthrough" shaders for
|
||||||
// shaders for untextured primitives vs images.
|
// untextured primitives vs images.
|
||||||
|
const GLubyte pix[] = {255, 255, 255, 255};
|
||||||
|
|
||||||
GLuint curtexture = state.boundTextures[state.curTextureUnit];
|
Texture::Filter filter;
|
||||||
|
filter.min = filter.mag = Texture::FILTER_NEAREST;
|
||||||
|
|
||||||
glGenTextures(1, &state.defaultTexture);
|
Texture::Wrap wrap;
|
||||||
bindTextureToUnit(state.defaultTexture, 0, false);
|
wrap.s = wrap.t = wrap.r = Texture::WRAP_CLAMP;
|
||||||
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
{
|
||||||
|
state.defaultTexture[i] = 0;
|
||||||
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
TextureType type = (TextureType) i;
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
||||||
|
|
||||||
GLubyte pix[] = {255, 255, 255, 255};
|
if (!isTextureTypeSupported(type))
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pix);
|
continue;
|
||||||
|
|
||||||
bindTextureToUnit(curtexture, 0, false);
|
GLuint curtexture = state.boundTextures[type][0];
|
||||||
|
|
||||||
|
glGenTextures(1, &state.defaultTexture[type]);
|
||||||
|
bindTextureToUnit(type, state.defaultTexture[type], 0, false);
|
||||||
|
|
||||||
|
setTextureWrap(type, wrap);
|
||||||
|
setTextureFilter(type, filter);
|
||||||
|
|
||||||
|
bool isSRGB = false;
|
||||||
|
rawTexStorage(type, 1, PIXELFORMAT_RGBA8, isSRGB, 1, 1);
|
||||||
|
|
||||||
|
TextureFormat fmt = convertPixelFormat(PIXELFORMAT_RGBA8, false, isSRGB);
|
||||||
|
|
||||||
|
int slices = type == TEXTURE_CUBE ? 6 : 1;
|
||||||
|
|
||||||
|
for (int slice = 0; slice < slices; slice++)
|
||||||
|
{
|
||||||
|
GLenum gltarget = getGLTextureType(type);
|
||||||
|
|
||||||
|
if (type == TEXTURE_CUBE)
|
||||||
|
gltarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
|
||||||
|
|
||||||
|
if (type == TEXTURE_2D || type == TEXTURE_CUBE)
|
||||||
|
glTexSubImage2D(gltarget, 0, 0, 0, 1, 1, fmt.externalformat, fmt.type, pix);
|
||||||
|
else if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
|
||||||
|
glTexSubImage3D(gltarget, 0, 0, 0, slice, 1, 1, 1, fmt.externalformat, fmt.type, pix);
|
||||||
|
}
|
||||||
|
|
||||||
|
bindTextureToUnit(type, curtexture, 0, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGL::prepareDraw()
|
void OpenGL::prepareDraw()
|
||||||
@@ -432,6 +503,27 @@ GLenum OpenGL::getGLBufferType(BufferType type)
|
|||||||
case BUFFER_MAX_ENUM:
|
case BUFFER_MAX_ENUM:
|
||||||
return GL_ZERO;
|
return GL_ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return GL_ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLenum OpenGL::getGLTextureType(TextureType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case TEXTURE_2D:
|
||||||
|
return GL_TEXTURE_2D;
|
||||||
|
case TEXTURE_VOLUME:
|
||||||
|
return GL_TEXTURE_3D;
|
||||||
|
case TEXTURE_2D_ARRAY:
|
||||||
|
return GL_TEXTURE_2D_ARRAY;
|
||||||
|
case TEXTURE_CUBE:
|
||||||
|
return GL_TEXTURE_CUBE_MAP;
|
||||||
|
case TEXTURE_MAX_ENUM:
|
||||||
|
return GL_ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GL_ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLenum OpenGL::getGLIndexDataType(IndexDataType type)
|
GLenum OpenGL::getGLIndexDataType(IndexDataType type)
|
||||||
@@ -651,6 +743,29 @@ void OpenGL::deleteFramebuffer(GLuint framebuffer)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenGL::framebufferTexture(GLenum attachment, TextureType texType, GLuint texture, int level, int layer, int face)
|
||||||
|
{
|
||||||
|
GLenum textarget = getGLTextureType(texType);
|
||||||
|
|
||||||
|
switch (texType)
|
||||||
|
{
|
||||||
|
case TEXTURE_2D:
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, textarget, texture, level);
|
||||||
|
break;
|
||||||
|
case TEXTURE_VOLUME:
|
||||||
|
glFramebufferTexture3D(GL_FRAMEBUFFER, attachment, textarget, texture, level, layer);
|
||||||
|
break;
|
||||||
|
case TEXTURE_2D_ARRAY:
|
||||||
|
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, texture, level, layer);
|
||||||
|
break;
|
||||||
|
case TEXTURE_CUBE:
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture, level);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void OpenGL::useProgram(GLuint program)
|
void OpenGL::useProgram(GLuint program)
|
||||||
{
|
{
|
||||||
glUseProgram(program);
|
glUseProgram(program);
|
||||||
@@ -670,9 +785,9 @@ GLuint OpenGL::getDefaultFBO() const
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
GLuint OpenGL::getDefaultTexture() const
|
GLuint OpenGL::getDefaultTexture(TextureType type) const
|
||||||
{
|
{
|
||||||
return state.defaultTexture;
|
return state.defaultTexture[type];
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGL::setTextureUnit(int textureunit)
|
void OpenGL::setTextureUnit(int textureunit)
|
||||||
@@ -683,16 +798,16 @@ void OpenGL::setTextureUnit(int textureunit)
|
|||||||
state.curTextureUnit = textureunit;
|
state.curTextureUnit = textureunit;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGL::bindTextureToUnit(GLuint texture, int textureunit, bool restoreprev)
|
void OpenGL::bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev)
|
||||||
{
|
{
|
||||||
if (texture != state.boundTextures[textureunit])
|
if (texture != state.boundTextures[target][textureunit])
|
||||||
{
|
{
|
||||||
int oldtextureunit = state.curTextureUnit;
|
int oldtextureunit = state.curTextureUnit;
|
||||||
if (oldtextureunit != textureunit)
|
if (oldtextureunit != textureunit)
|
||||||
glActiveTexture(GL_TEXTURE0 + textureunit);
|
glActiveTexture(GL_TEXTURE0 + textureunit);
|
||||||
|
|
||||||
state.boundTextures[textureunit] = texture;
|
state.boundTextures[target][textureunit] = texture;
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
glBindTexture(getGLTextureType(target), texture);
|
||||||
|
|
||||||
if (restoreprev && oldtextureunit != textureunit)
|
if (restoreprev && oldtextureunit != textureunit)
|
||||||
glActiveTexture(GL_TEXTURE0 + oldtextureunit);
|
glActiveTexture(GL_TEXTURE0 + oldtextureunit);
|
||||||
@@ -703,24 +818,29 @@ void OpenGL::bindTextureToUnit(GLuint texture, int textureunit, bool restoreprev
|
|||||||
|
|
||||||
void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev)
|
void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev)
|
||||||
{
|
{
|
||||||
GLuint handle = texture != nullptr ? (GLuint) texture->getHandle() : getDefaultTexture();
|
GLuint handle = texture != nullptr ? (GLuint) texture->getHandle() : getDefaultTexture(TEXTURE_2D);
|
||||||
bindTextureToUnit(handle, textureunit, restoreprev);
|
TextureType textype = texture != nullptr ? texture->getTextureType() : TEXTURE_2D;
|
||||||
|
|
||||||
|
bindTextureToUnit(textype, handle, textureunit, restoreprev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGL::deleteTexture(GLuint texture)
|
void OpenGL::deleteTexture(GLuint texture)
|
||||||
{
|
{
|
||||||
// glDeleteTextures binds texture 0 to all texture units the deleted texture
|
// glDeleteTextures binds texture 0 to all texture units the deleted texture
|
||||||
// was bound to before deletion.
|
// was bound to before deletion.
|
||||||
for (GLuint &texid : state.boundTextures)
|
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
|
||||||
|
{
|
||||||
|
for (GLuint &texid : state.boundTextures[i])
|
||||||
{
|
{
|
||||||
if (texid == texture)
|
if (texid == texture)
|
||||||
texid = 0;
|
texid = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
glDeleteTextures(1, &texture);
|
glDeleteTextures(1, &texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGL::setTextureFilter(graphics::Texture::Filter &f)
|
void OpenGL::setTextureFilter(TextureType target, graphics::Texture::Filter &f)
|
||||||
{
|
{
|
||||||
GLint gmin, gmag;
|
GLint gmin, gmag;
|
||||||
|
|
||||||
@@ -756,13 +876,15 @@ void OpenGL::setTextureFilter(graphics::Texture::Filter &f)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gmin);
|
GLenum gltarget = getGLTextureType(target);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gmag);
|
|
||||||
|
glTexParameteri(gltarget, GL_TEXTURE_MIN_FILTER, gmin);
|
||||||
|
glTexParameteri(gltarget, GL_TEXTURE_MAG_FILTER, gmag);
|
||||||
|
|
||||||
if (GLAD_EXT_texture_filter_anisotropic)
|
if (GLAD_EXT_texture_filter_anisotropic)
|
||||||
{
|
{
|
||||||
f.anisotropy = std::min(std::max(f.anisotropy, 1.0f), maxAnisotropy);
|
f.anisotropy = std::min(std::max(f.anisotropy, 1.0f), maxAnisotropy);
|
||||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, f.anisotropy);
|
glTexParameterf(gltarget, GL_TEXTURE_MAX_ANISOTROPY_EXT, f.anisotropy);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
f.anisotropy = 1.0f;
|
f.anisotropy = 1.0f;
|
||||||
@@ -785,10 +907,104 @@ GLint OpenGL::getGLWrapMode(Texture::WrapMode wmode)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGL::setTextureWrap(const graphics::Texture::Wrap &w)
|
void OpenGL::setTextureWrap(TextureType target, const graphics::Texture::Wrap &w)
|
||||||
{
|
{
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, getGLWrapMode(w.s));
|
glTexParameteri(getGLTextureType(target), GL_TEXTURE_WRAP_S, getGLWrapMode(w.s));
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, getGLWrapMode(w.t));
|
glTexParameteri(getGLTextureType(target), GL_TEXTURE_WRAP_T, getGLWrapMode(w.t));
|
||||||
|
|
||||||
|
if (target == TEXTURE_VOLUME)
|
||||||
|
glTexParameteri(getGLTextureType(target), GL_TEXTURE_WRAP_R, getGLWrapMode(w.r));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGL::rawTexStorage(TextureType target, int levels, PixelFormat pixelformat, bool &isSRGB, int width, int height, int depth)
|
||||||
|
{
|
||||||
|
GLenum gltarget = getGLTextureType(target);
|
||||||
|
TextureFormat fmt = convertPixelFormat(pixelformat, false, isSRGB);
|
||||||
|
|
||||||
|
if (fmt.swizzled)
|
||||||
|
{
|
||||||
|
glTexParameteri(gltarget, GL_TEXTURE_SWIZZLE_R, fmt.swizzle[0]);
|
||||||
|
glTexParameteri(gltarget, GL_TEXTURE_SWIZZLE_G, fmt.swizzle[1]);
|
||||||
|
glTexParameteri(gltarget, GL_TEXTURE_SWIZZLE_B, fmt.swizzle[2]);
|
||||||
|
glTexParameteri(gltarget, GL_TEXTURE_SWIZZLE_A, fmt.swizzle[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool supportsTexStorage = GLAD_VERSION_4_2 || GLAD_ARB_texture_storage;
|
||||||
|
|
||||||
|
// Apparently there are bugs with glTexStorage on some Android drivers. I'd
|
||||||
|
// rather not find out the hard way, so we'll avoid it for now...
|
||||||
|
#ifndef LOVE_ANDROID
|
||||||
|
if (GLAD_ES_VERSION_3_0)
|
||||||
|
supportsTexStorage = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (supportsTexStorage)
|
||||||
|
{
|
||||||
|
if (target == TEXTURE_2D || target == TEXTURE_CUBE)
|
||||||
|
glTexStorage2D(gltarget, levels, fmt.internalformat, width, height);
|
||||||
|
else if (target == TEXTURE_VOLUME || target == TEXTURE_2D_ARRAY)
|
||||||
|
glTexStorage3D(gltarget, levels, fmt.internalformat, width, height, depth);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int w = width;
|
||||||
|
int h = height;
|
||||||
|
int d = depth;
|
||||||
|
|
||||||
|
for (int level = 0; level < levels; level++)
|
||||||
|
{
|
||||||
|
if (target == TEXTURE_2D || target == TEXTURE_CUBE)
|
||||||
|
{
|
||||||
|
int faces = target == TEXTURE_CUBE ? 6 : 1;
|
||||||
|
for (int face = 0; face < faces; face++)
|
||||||
|
{
|
||||||
|
if (target == TEXTURE_CUBE)
|
||||||
|
gltarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
|
||||||
|
|
||||||
|
glTexImage2D(gltarget, level, fmt.internalformat, w, h, 0,
|
||||||
|
fmt.externalformat, fmt.type, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (target == TEXTURE_2D_ARRAY || target == TEXTURE_VOLUME)
|
||||||
|
{
|
||||||
|
if (target == TEXTURE_VOLUME && GLAD_ES_VERSION_2_0 && GLAD_OES_texture_3D && !GLAD_ES_VERSION_3_0)
|
||||||
|
{
|
||||||
|
glTexImage3DOES(gltarget, level, fmt.internalformat, w, h,
|
||||||
|
d, 0, fmt.externalformat, fmt.type, nullptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glTexImage3D(gltarget, level, fmt.internalformat, w, h, d,
|
||||||
|
0, fmt.externalformat, fmt.type, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w = std::max(w / 2, 1);
|
||||||
|
h = std::max(h / 2, 1);
|
||||||
|
|
||||||
|
if (target == TEXTURE_VOLUME)
|
||||||
|
d = std::max(d / 2, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gltarget != GL_ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGL::isTextureTypeSupported(TextureType type) const
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case TEXTURE_2D:
|
||||||
|
return true;
|
||||||
|
case TEXTURE_VOLUME:
|
||||||
|
return GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_3D;
|
||||||
|
case TEXTURE_2D_ARRAY:
|
||||||
|
return GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0 || GLAD_EXT_texture_array;
|
||||||
|
case TEXTURE_CUBE:
|
||||||
|
return GLAD_VERSION_1_3 || GLAD_ES_VERSION_2_0;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGL::isClampZeroTextureWrapSupported() const
|
bool OpenGL::isClampZeroTextureWrapSupported() const
|
||||||
@@ -807,9 +1023,24 @@ bool OpenGL::isInstancingSupported() const
|
|||||||
|| GLAD_ARB_instanced_arrays || GLAD_EXT_instanced_arrays || GLAD_ANGLE_instanced_arrays;
|
|| GLAD_ARB_instanced_arrays || GLAD_EXT_instanced_arrays || GLAD_ANGLE_instanced_arrays;
|
||||||
}
|
}
|
||||||
|
|
||||||
int OpenGL::getMaxTextureSize() const
|
int OpenGL::getMax2DTextureSize() const
|
||||||
{
|
{
|
||||||
return maxTextureSize;
|
return std::max(max2DTextureSize, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int OpenGL::getMax3DTextureSize() const
|
||||||
|
{
|
||||||
|
return std::max(max3DTextureSize, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int OpenGL::getMaxCubeTextureSize() const
|
||||||
|
{
|
||||||
|
return std::max(maxCubeTextureSize, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int OpenGL::getMaxTextureLayers() const
|
||||||
|
{
|
||||||
|
return std::max(maxTextureArrayLayers, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int OpenGL::getMaxRenderTargets() const
|
int OpenGL::getMaxRenderTargets() const
|
||||||
|
|||||||
@@ -248,6 +248,8 @@ public:
|
|||||||
GLuint getFramebuffer(FramebufferTarget target) const;
|
GLuint getFramebuffer(FramebufferTarget target) const;
|
||||||
void deleteFramebuffer(GLuint framebuffer);
|
void deleteFramebuffer(GLuint framebuffer);
|
||||||
|
|
||||||
|
void framebufferTexture(GLenum attachment, TextureType texType, GLuint texture, int level, int layer = 0, int face = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls glUseProgram.
|
* Calls glUseProgram.
|
||||||
**/
|
**/
|
||||||
@@ -262,7 +264,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Gets the ID for love's default texture (used for "untextured" primitives.)
|
* Gets the ID for love's default texture (used for "untextured" primitives.)
|
||||||
**/
|
**/
|
||||||
GLuint getDefaultTexture() const;
|
GLuint getDefaultTexture(TextureType type) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper for setting the active texture unit.
|
* Helper for setting the active texture unit.
|
||||||
@@ -277,7 +279,7 @@ public:
|
|||||||
* @param textureunit Index in the range of [0, maxtextureunits-1]
|
* @param textureunit Index in the range of [0, maxtextureunits-1]
|
||||||
* @param restoreprev Restore previously bound texture unit when done.
|
* @param restoreprev Restore previously bound texture unit when done.
|
||||||
**/
|
**/
|
||||||
void bindTextureToUnit(GLuint texture, int textureunit, bool restoreprev);
|
void bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev);
|
||||||
void bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev);
|
void bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -291,13 +293,21 @@ public:
|
|||||||
* The anisotropy parameter of the argument is set to the actual amount of
|
* The anisotropy parameter of the argument is set to the actual amount of
|
||||||
* anisotropy that was used.
|
* anisotropy that was used.
|
||||||
**/
|
**/
|
||||||
void setTextureFilter(graphics::Texture::Filter &f);
|
void setTextureFilter(TextureType target, graphics::Texture::Filter &f);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the texture wrap mode for the currently bound texture.
|
* Sets the texture wrap mode for the currently bound texture.
|
||||||
**/
|
**/
|
||||||
void setTextureWrap(const graphics::Texture::Wrap &w);
|
void setTextureWrap(TextureType target, const graphics::Texture::Wrap &w);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equivalent to glTexStorage2D/3D on platforms that support it. Equivalent
|
||||||
|
* to glTexImage2D/3D for all levels and slices of a texture otherwise.
|
||||||
|
* NOTE: this does not handle compressed texture formats.
|
||||||
|
**/
|
||||||
|
bool rawTexStorage(TextureType target, int levels, PixelFormat pixelformat, bool &isSRGB, int width, int height, int depth = 1);
|
||||||
|
|
||||||
|
bool isTextureTypeSupported(TextureType type) const;
|
||||||
bool isClampZeroTextureWrapSupported() const;
|
bool isClampZeroTextureWrapSupported() const;
|
||||||
bool isPixelShaderHighpSupported() const;
|
bool isPixelShaderHighpSupported() const;
|
||||||
bool isInstancingSupported() const;
|
bool isInstancingSupported() const;
|
||||||
@@ -305,7 +315,10 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Returns the maximum supported width or height of a texture.
|
* Returns the maximum supported width or height of a texture.
|
||||||
**/
|
**/
|
||||||
int getMaxTextureSize() const;
|
int getMax2DTextureSize() const;
|
||||||
|
int getMax3DTextureSize() const;
|
||||||
|
int getMaxCubeTextureSize() const;
|
||||||
|
int getMaxTextureLayers() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the maximum supported number of simultaneous render targets.
|
* Returns the maximum supported number of simultaneous render targets.
|
||||||
@@ -349,6 +362,7 @@ public:
|
|||||||
static GLenum getGLBufferType(BufferType type);
|
static GLenum getGLBufferType(BufferType type);
|
||||||
static GLenum getGLIndexDataType(IndexDataType type);
|
static GLenum getGLIndexDataType(IndexDataType type);
|
||||||
static GLenum getGLBufferUsage(vertex::Usage usage);
|
static GLenum getGLBufferUsage(vertex::Usage usage);
|
||||||
|
static GLenum getGLTextureType(TextureType type);
|
||||||
static GLint getGLWrapMode(Texture::WrapMode wmode);
|
static GLint getGLWrapMode(Texture::WrapMode wmode);
|
||||||
|
|
||||||
static TextureFormat convertPixelFormat(PixelFormat pixelformat, bool renderbuffer, bool &isSRGB);
|
static TextureFormat convertPixelFormat(PixelFormat pixelformat, bool renderbuffer, bool &isSRGB);
|
||||||
@@ -374,7 +388,10 @@ private:
|
|||||||
|
|
||||||
bool pixelShaderHighpSupported;
|
bool pixelShaderHighpSupported;
|
||||||
float maxAnisotropy;
|
float maxAnisotropy;
|
||||||
int maxTextureSize;
|
int max2DTextureSize;
|
||||||
|
int max3DTextureSize;
|
||||||
|
int maxCubeTextureSize;
|
||||||
|
int maxTextureArrayLayers;
|
||||||
int maxRenderTargets;
|
int maxRenderTargets;
|
||||||
int maxRenderbufferSamples;
|
int maxRenderbufferSamples;
|
||||||
int maxTextureUnits;
|
int maxTextureUnits;
|
||||||
@@ -390,7 +407,7 @@ private:
|
|||||||
GLuint boundBuffers[BUFFER_MAX_ENUM];
|
GLuint boundBuffers[BUFFER_MAX_ENUM];
|
||||||
|
|
||||||
// Texture unit state (currently bound texture for each texture unit.)
|
// Texture unit state (currently bound texture for each texture unit.)
|
||||||
std::vector<GLuint> boundTextures;
|
std::vector<GLuint> boundTextures[TEXTURE_MAX_ENUM];
|
||||||
|
|
||||||
// Currently active texture unit.
|
// Currently active texture unit.
|
||||||
int curTextureUnit;
|
int curTextureUnit;
|
||||||
@@ -410,7 +427,7 @@ private:
|
|||||||
|
|
||||||
bool framebufferSRGBEnabled;
|
bool framebufferSRGBEnabled;
|
||||||
|
|
||||||
GLuint defaultTexture;
|
GLuint defaultTexture[TEXTURE_MAX_ENUM];
|
||||||
|
|
||||||
} state;
|
} state;
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
|
|||||||
if (!prepareDraw(gfx, m))
|
if (!prepareDraw(gfx, m))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (Shader::current && texture.get())
|
||||||
|
Shader::current->checkMainTextureType(texture->getTextureType());
|
||||||
|
|
||||||
OpenGL::TempDebugGroup debuggroup("ParticleSystem draw");
|
OpenGL::TempDebugGroup debuggroup("ParticleSystem draw");
|
||||||
|
|
||||||
gl.bindTextureToUnit(texture, 0, false);
|
gl.bindTextureToUnit(texture, 0, false);
|
||||||
|
|||||||
@@ -40,11 +40,11 @@ Shader::Shader(const ShaderSource &source)
|
|||||||
: love::graphics::Shader(source)
|
: love::graphics::Shader(source)
|
||||||
, program(0)
|
, program(0)
|
||||||
, builtinUniforms()
|
, builtinUniforms()
|
||||||
|
, builtinUniformInfo()
|
||||||
, builtinAttributes()
|
, builtinAttributes()
|
||||||
, canvasWasActive(false)
|
, canvasWasActive(false)
|
||||||
, lastViewport()
|
, lastViewport()
|
||||||
, lastPointSize(0.0f)
|
, lastPointSize(0.0f)
|
||||||
, videoTextureUnits()
|
|
||||||
{
|
{
|
||||||
// load shader source and create program object
|
// load shader source and create program object
|
||||||
loadVolatile();
|
loadVolatile();
|
||||||
@@ -142,7 +142,10 @@ void Shader::mapActiveUniforms()
|
|||||||
{
|
{
|
||||||
// Built-in uniform locations default to -1 (nonexistent.)
|
// Built-in uniform locations default to -1 (nonexistent.)
|
||||||
for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
|
for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
|
||||||
|
{
|
||||||
builtinUniforms[i] = -1;
|
builtinUniforms[i] = -1;
|
||||||
|
builtinUniformInfo[i] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
GLint activeprogram = 0;
|
GLint activeprogram = 0;
|
||||||
glGetIntegerv(GL_CURRENT_PROGRAM, &activeprogram);
|
glGetIntegerv(GL_CURRENT_PROGRAM, &activeprogram);
|
||||||
@@ -158,17 +161,18 @@ void Shader::mapActiveUniforms()
|
|||||||
std::map<std::string, UniformInfo> olduniforms = uniforms;
|
std::map<std::string, UniformInfo> olduniforms = uniforms;
|
||||||
uniforms.clear();
|
uniforms.clear();
|
||||||
|
|
||||||
for (int i = 0; i < numuniforms; i++)
|
for (int uindex = 0; uindex < numuniforms; uindex++)
|
||||||
{
|
{
|
||||||
GLsizei namelen = 0;
|
GLsizei namelen = 0;
|
||||||
GLenum gltype = 0;
|
GLenum gltype = 0;
|
||||||
UniformInfo u = {};
|
UniformInfo u = {};
|
||||||
|
|
||||||
glGetActiveUniform(program, (GLuint) i, bufsize, &namelen, &u.count, &gltype, cname);
|
glGetActiveUniform(program, (GLuint) uindex, bufsize, &namelen, &u.count, &gltype, cname);
|
||||||
|
|
||||||
u.name = std::string(cname, (size_t) namelen);
|
u.name = std::string(cname, (size_t) namelen);
|
||||||
u.location = glGetUniformLocation(program, u.name.c_str());
|
u.location = glGetUniformLocation(program, u.name.c_str());
|
||||||
u.baseType = getUniformBaseType(gltype);
|
u.baseType = getUniformBaseType(gltype);
|
||||||
|
u.textureType = getUniformTextureType(gltype);
|
||||||
|
|
||||||
if (u.baseType == UNIFORM_MATRIX)
|
if (u.baseType == UNIFORM_MATRIX)
|
||||||
u.matrix = getMatrixSize(gltype);
|
u.matrix = getMatrixSize(gltype);
|
||||||
@@ -184,13 +188,24 @@ void Shader::mapActiveUniforms()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If this is a built-in (LOVE-created) uniform, store the location.
|
// If this is a built-in (LOVE-created) uniform, store the location.
|
||||||
BuiltinUniform builtin;
|
BuiltinUniform builtin = BUILTIN_MAX_ENUM;
|
||||||
if (getConstant(u.name.c_str(), builtin))
|
if (getConstant(u.name.c_str(), builtin))
|
||||||
builtinUniforms[int(builtin)] = u.location;
|
builtinUniforms[int(builtin)] = u.location;
|
||||||
|
|
||||||
if (u.location == -1)
|
if (u.location == -1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (u.baseType == UNIFORM_SAMPLER && builtin != BUILTIN_TEXTURE_MAIN)
|
||||||
|
{
|
||||||
|
TextureUnit unit;
|
||||||
|
unit.type = u.textureType;
|
||||||
|
unit.active = true;
|
||||||
|
unit.texture = gl.getDefaultTexture(u.textureType);
|
||||||
|
|
||||||
|
for (int i = 0; i < u.count; i++)
|
||||||
|
textureUnits.push_back(unit);
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure previously set uniform data is preserved, and shader-
|
// Make sure previously set uniform data is preserved, and shader-
|
||||||
// initialized values are retrieved.
|
// initialized values are retrieved.
|
||||||
auto oldu = olduniforms.find(u.name);
|
auto oldu = olduniforms.find(u.name);
|
||||||
@@ -200,23 +215,6 @@ void Shader::mapActiveUniforms()
|
|||||||
u.textures = oldu->second.textures;
|
u.textures = oldu->second.textures;
|
||||||
|
|
||||||
updateUniform(&u, u.count, true);
|
updateUniform(&u, u.count, true);
|
||||||
|
|
||||||
if (u.baseType == UNIFORM_SAMPLER)
|
|
||||||
{
|
|
||||||
// Make sure all stored textures have their Volatiles loaded
|
|
||||||
// before the sendTextures call, since it calls getHandle().
|
|
||||||
for (int i = 0; i < u.count; i++)
|
|
||||||
{
|
|
||||||
if (u.textures[i] == nullptr)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Volatile *v = dynamic_cast<Volatile *>(u.textures[i]);
|
|
||||||
if (v != nullptr)
|
|
||||||
v->loadVolatile();
|
|
||||||
}
|
|
||||||
|
|
||||||
sendTextures(&u, u.textures, u.count, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -252,9 +250,14 @@ void Shader::mapActiveUniforms()
|
|||||||
|
|
||||||
if (u.baseType == UNIFORM_SAMPLER)
|
if (u.baseType == UNIFORM_SAMPLER)
|
||||||
{
|
{
|
||||||
// Initialize all samplers to 0. Both GLSL and GLSL ES are
|
int startunit = (int) textureUnits.size() - u.count;
|
||||||
// supposed to do this themselves, but some Android devices
|
|
||||||
// (galaxy tab 3 and 4) don't seem to do it...
|
if (builtin == BUILTIN_TEXTURE_MAIN)
|
||||||
|
startunit = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < u.count; i++)
|
||||||
|
u.ints[i] = startunit + i;
|
||||||
|
|
||||||
glUniform1iv(u.location, u.count, u.ints);
|
glUniform1iv(u.location, u.count, u.ints);
|
||||||
|
|
||||||
u.textures = new Texture*[u.count];
|
u.textures = new Texture*[u.count];
|
||||||
@@ -307,6 +310,26 @@ void Shader::mapActiveUniforms()
|
|||||||
}
|
}
|
||||||
|
|
||||||
uniforms[u.name] = u;
|
uniforms[u.name] = u;
|
||||||
|
|
||||||
|
if (builtin != BUILTIN_MAX_ENUM)
|
||||||
|
builtinUniformInfo[(int)builtin] = &uniforms[u.name];
|
||||||
|
|
||||||
|
if (u.baseType == UNIFORM_SAMPLER)
|
||||||
|
{
|
||||||
|
// Make sure all stored textures have their Volatiles loaded before
|
||||||
|
// the sendTextures call, since it calls getHandle().
|
||||||
|
for (int i = 0; i < u.count; i++)
|
||||||
|
{
|
||||||
|
if (u.textures[i] == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Volatile *v = dynamic_cast<Volatile *>(u.textures[i]);
|
||||||
|
if (v != nullptr)
|
||||||
|
v->loadVolatile();
|
||||||
|
}
|
||||||
|
|
||||||
|
sendTextures(&u, u.textures, u.count, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure uniforms that existed before but don't exist anymore are
|
// Make sure uniforms that existed before but don't exist anymore are
|
||||||
@@ -348,12 +371,9 @@ bool Shader::loadVolatile()
|
|||||||
lastProjectionMatrix.setTranslation(nan, nan);
|
lastProjectionMatrix.setTranslation(nan, nan);
|
||||||
lastTransformMatrix.setTranslation(nan, nan);
|
lastTransformMatrix.setTranslation(nan, nan);
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++)
|
|
||||||
videoTextureUnits[i] = 0;
|
|
||||||
|
|
||||||
// zero out active texture list
|
// zero out active texture list
|
||||||
textureUnits.clear();
|
textureUnits.clear();
|
||||||
textureUnits.resize(gl.getMaxTextureUnits(), TextureUnit());
|
textureUnits.push_back(TextureUnit());
|
||||||
|
|
||||||
std::vector<GLuint> shaderids;
|
std::vector<GLuint> shaderids;
|
||||||
|
|
||||||
@@ -449,7 +469,7 @@ void Shader::unloadVolatile()
|
|||||||
|
|
||||||
// active texture list is probably invalid, clear it
|
// active texture list is probably invalid, clear it
|
||||||
textureUnits.clear();
|
textureUnits.clear();
|
||||||
textureUnits.resize(gl.getMaxTextureUnits(), TextureUnit());
|
textureUnits.push_back(TextureUnit());
|
||||||
|
|
||||||
attributes.clear();
|
attributes.clear();
|
||||||
|
|
||||||
@@ -497,7 +517,7 @@ std::string Shader::getWarnings() const
|
|||||||
return warnings;
|
return warnings;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shader::attach(bool temporary)
|
void Shader::attach()
|
||||||
{
|
{
|
||||||
if (current != this)
|
if (current != this)
|
||||||
{
|
{
|
||||||
@@ -505,23 +525,20 @@ void Shader::attach(bool temporary)
|
|||||||
current = this;
|
current = this;
|
||||||
// retain/release happens in Graphics::setShader.
|
// retain/release happens in Graphics::setShader.
|
||||||
|
|
||||||
if (!temporary)
|
// Make sure all textures are bound to their respective texture units.
|
||||||
|
for (int i = 0; i < (int) textureUnits.size(); ++i)
|
||||||
{
|
{
|
||||||
// Make sure all textures are properly bound to their respective
|
const TextureUnit &unit = textureUnits[i];
|
||||||
// texture units.
|
if (unit.active)
|
||||||
for (int i = 1; i < (int) textureUnits.size(); ++i)
|
gl.bindTextureToUnit(unit.type, unit.texture, i, false);
|
||||||
{
|
|
||||||
if (textureUnits[i].active)
|
|
||||||
gl.bindTextureToUnit(textureUnits[i].texture, i, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// send any pending uniforms to the shader program.
|
// send any pending uniforms to the shader program.
|
||||||
for (const auto &p : pendingUniformUpdates)
|
for (const auto &p : pendingUniformUpdates)
|
||||||
updateUniform(p.first, p.second);
|
updateUniform(p.first, p.second, true);
|
||||||
|
|
||||||
pendingUniformUpdates.clear();
|
pendingUniformUpdates.clear();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const
|
const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const
|
||||||
@@ -534,15 +551,25 @@ const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const
|
|||||||
return &(it->second);
|
return &(it->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shader::updateUniform(const UniformInfo *info, int count, bool internalUpdate)
|
const Shader::UniformInfo *Shader::getUniformInfo(BuiltinUniform builtin) const
|
||||||
{
|
{
|
||||||
if (current != this)
|
return builtinUniformInfo[(int)builtin];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shader::updateUniform(const UniformInfo *info, int count)
|
||||||
|
{
|
||||||
|
updateUniform(info, count, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shader::updateUniform(const UniformInfo *info, int count, bool internalupdate)
|
||||||
|
{
|
||||||
|
if (current != this && !internalupdate)
|
||||||
{
|
{
|
||||||
pendingUniformUpdates.push_back(std::make_pair(info, count));
|
pendingUniformUpdates.push_back(std::make_pair(info, count));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!internalUpdate)
|
if (!internalupdate)
|
||||||
flushStreamDraws();
|
flushStreamDraws();
|
||||||
|
|
||||||
int location = info->location;
|
int location = info->location;
|
||||||
@@ -628,24 +655,9 @@ void Shader::updateUniform(const UniformInfo *info, int count, bool internalUpda
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shader::getFreeTextureUnits(int count)
|
void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count)
|
||||||
{
|
{
|
||||||
int startunit = -1;
|
Shader::sendTextures(info, textures, count, false);
|
||||||
|
|
||||||
// Ignore the first texture unit for Shader-local texture bindings.
|
|
||||||
for (int i = 1; i < (int) textureUnits.size(); i++)
|
|
||||||
{
|
|
||||||
if (!textureUnits[i].active && i + count <= (int) textureUnits.size())
|
|
||||||
{
|
|
||||||
startunit = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startunit == -1)
|
|
||||||
throw love::Exception("No more texture units available for shader.");
|
|
||||||
|
|
||||||
return startunit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate)
|
void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate)
|
||||||
@@ -659,56 +671,37 @@ void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count
|
|||||||
flushStreamDraws();
|
flushStreamDraws();
|
||||||
|
|
||||||
count = std::min(count, info->count);
|
count = std::min(count, info->count);
|
||||||
bool updateuniform = false;
|
|
||||||
|
|
||||||
// Make sure the shader's samplers are associated with texture units.
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
if (info->ints[i] == 0 && textures[i] != nullptr)
|
|
||||||
{
|
|
||||||
int texunit = getFreeTextureUnits(1);
|
|
||||||
textureUnits[texunit].active = true;
|
|
||||||
|
|
||||||
info->ints[i] = texunit;
|
|
||||||
updateuniform = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updateuniform)
|
|
||||||
updateUniform(info, count, internalUpdate);
|
|
||||||
|
|
||||||
// Bind the textures to the texture units.
|
// Bind the textures to the texture units.
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
if (textures[i] != nullptr)
|
if (textures[i] != nullptr)
|
||||||
|
{
|
||||||
|
if (textures[i]->getTextureType() != info->textureType)
|
||||||
|
continue;
|
||||||
|
|
||||||
textures[i]->retain();
|
textures[i]->retain();
|
||||||
|
}
|
||||||
|
|
||||||
if (info->textures[i] != nullptr)
|
if (info->textures[i] != nullptr)
|
||||||
info->textures[i]->release();
|
info->textures[i]->release();
|
||||||
|
|
||||||
info->textures[i] = textures[i];
|
info->textures[i] = textures[i];
|
||||||
|
|
||||||
|
GLuint gltex = 0;
|
||||||
|
if (textures[i] != nullptr)
|
||||||
|
gltex = (GLuint) textures[i]->getHandle();
|
||||||
|
else
|
||||||
|
gltex = gl.getDefaultTexture(info->textureType);
|
||||||
|
|
||||||
int texunit = info->ints[i];
|
int texunit = info->ints[i];
|
||||||
|
|
||||||
if (textures[i] != nullptr)
|
|
||||||
{
|
|
||||||
GLuint gltex = (GLuint) textures[i]->getHandle();
|
|
||||||
|
|
||||||
if (shaderactive)
|
if (shaderactive)
|
||||||
gl.bindTextureToUnit(gltex, texunit, false);
|
gl.bindTextureToUnit(info->textureType, gltex, texunit, false);
|
||||||
|
|
||||||
// Store texture id so it can be re-bound to the texture unit later.
|
// Store texture id so it can be re-bound to the texture unit later.
|
||||||
textureUnits[texunit].texture = gltex;
|
textureUnits[texunit].texture = gltex;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (shaderactive)
|
|
||||||
gl.bindTextureToUnit((GLuint) 0, texunit, false);
|
|
||||||
|
|
||||||
textureUnits[texunit].texture = 0;
|
|
||||||
textureUnits[texunit].active = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shader::flushStreamDraws() const
|
void Shader::flushStreamDraws() const
|
||||||
@@ -743,50 +736,22 @@ GLint Shader::getAttribLocation(const std::string &name)
|
|||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shader::setVideoTextures(ptrdiff_t ytexture, ptrdiff_t cbtexture, ptrdiff_t crtexture)
|
void Shader::setVideoTextures(Texture *ytexture, Texture *cbtexture, Texture *crtexture)
|
||||||
{
|
{
|
||||||
// Set up the texture units that will be used by the shader to sample from
|
|
||||||
// the textures, if they haven't been set up yet.
|
|
||||||
if (videoTextureUnits[0] == 0)
|
|
||||||
{
|
|
||||||
const BuiltinUniform builtins[3] = {
|
const BuiltinUniform builtins[3] = {
|
||||||
BUILTIN_TEXTURE_VIDEO_Y,
|
BUILTIN_TEXTURE_VIDEO_Y,
|
||||||
BUILTIN_TEXTURE_VIDEO_CB,
|
BUILTIN_TEXTURE_VIDEO_CB,
|
||||||
BUILTIN_TEXTURE_VIDEO_CR,
|
BUILTIN_TEXTURE_VIDEO_CR,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Texture *textures[3] = {ytexture, cbtexture, crtexture};
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
GLint loc = builtinUniforms[builtins[i]];
|
const UniformInfo *info = builtinUniformInfo[builtins[i]];
|
||||||
const char *name = nullptr;;
|
|
||||||
|
|
||||||
if (loc >= 0 && getConstant(builtins[i], name) && name != nullptr)
|
if (info != nullptr)
|
||||||
{
|
sendTextures(info, &textures[i], 1);
|
||||||
const UniformInfo *info = getUniformInfo(name);
|
|
||||||
if (info == nullptr)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
videoTextureUnits[i] = getFreeTextureUnits(1);
|
|
||||||
textureUnits[videoTextureUnits[i]].active = true;
|
|
||||||
|
|
||||||
info->ints[0] = videoTextureUnits[i];
|
|
||||||
updateUniform(info, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const GLuint textures[3] = {(GLuint) ytexture, (GLuint) cbtexture, (GLuint) crtexture};
|
|
||||||
|
|
||||||
// Bind the textures to their respective texture units.
|
|
||||||
for (int i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
if (videoTextureUnits[i] != 0)
|
|
||||||
{
|
|
||||||
// Store texture id so it can be re-bound later.
|
|
||||||
textureUnits[videoTextureUnits[i]].texture = textures[i];
|
|
||||||
if (current == this)
|
|
||||||
gl.bindTextureToUnit(textures[i], videoTextureUnits[i], false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -925,15 +890,15 @@ bool Shader::isSupported()
|
|||||||
|
|
||||||
int Shader::getUniformTypeComponents(GLenum type) const
|
int Shader::getUniformTypeComponents(GLenum type) const
|
||||||
{
|
{
|
||||||
|
if (getUniformBaseType(type) == UNIFORM_SAMPLER)
|
||||||
|
return 1;
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case GL_INT:
|
case GL_INT:
|
||||||
case GL_UNSIGNED_INT:
|
case GL_UNSIGNED_INT:
|
||||||
case GL_FLOAT:
|
case GL_FLOAT:
|
||||||
case GL_BOOL:
|
case GL_BOOL:
|
||||||
case GL_SAMPLER_1D:
|
|
||||||
case GL_SAMPLER_2D:
|
|
||||||
case GL_SAMPLER_3D:
|
|
||||||
return 1;
|
return 1;
|
||||||
case GL_INT_VEC2:
|
case GL_INT_VEC2:
|
||||||
case GL_UNSIGNED_INT_VEC2:
|
case GL_UNSIGNED_INT_VEC2:
|
||||||
@@ -1059,6 +1024,44 @@ Shader::UniformType Shader::getUniformBaseType(GLenum type) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextureType Shader::getUniformTextureType(GLenum type) const
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case GL_SAMPLER_1D:
|
||||||
|
case GL_SAMPLER_1D_SHADOW:
|
||||||
|
case GL_SAMPLER_1D_ARRAY:
|
||||||
|
case GL_SAMPLER_1D_ARRAY_SHADOW:
|
||||||
|
// 1D-typed textures are not supported.
|
||||||
|
return TEXTURE_MAX_ENUM;
|
||||||
|
case GL_SAMPLER_2D:
|
||||||
|
//case GL_SAMPLER_2D_SHADOW:
|
||||||
|
return TEXTURE_2D;
|
||||||
|
case GL_SAMPLER_2D_MULTISAMPLE:
|
||||||
|
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
|
||||||
|
// Multisample textures are not supported.
|
||||||
|
return TEXTURE_MAX_ENUM;
|
||||||
|
case GL_SAMPLER_2D_RECT:
|
||||||
|
case GL_SAMPLER_2D_RECT_SHADOW:
|
||||||
|
// Rectangle textures are not supported.
|
||||||
|
return TEXTURE_MAX_ENUM;
|
||||||
|
case GL_SAMPLER_2D_ARRAY:
|
||||||
|
//case GL_SAMPLER_2D_ARRAY_SHADOW:
|
||||||
|
return TEXTURE_2D_ARRAY;
|
||||||
|
case GL_SAMPLER_3D:
|
||||||
|
return TEXTURE_VOLUME;
|
||||||
|
case GL_SAMPLER_CUBE:
|
||||||
|
//case GL_SAMPLER_CUBE_SHADOW:
|
||||||
|
return TEXTURE_CUBE;
|
||||||
|
case GL_SAMPLER_CUBE_MAP_ARRAY:
|
||||||
|
case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
|
||||||
|
// Cubemap array textures are not supported.
|
||||||
|
return TEXTURE_MAX_ENUM;
|
||||||
|
default:
|
||||||
|
return TEXTURE_MAX_ENUM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
} // graphics
|
} // graphics
|
||||||
} // love
|
} // love
|
||||||
|
|||||||
@@ -56,14 +56,15 @@ public:
|
|||||||
void unloadVolatile() override;
|
void unloadVolatile() override;
|
||||||
|
|
||||||
// Implements Shader.
|
// Implements Shader.
|
||||||
void attach(bool temporary = false) override;
|
void attach() override;
|
||||||
std::string getWarnings() const override;
|
std::string getWarnings() const override;
|
||||||
const UniformInfo *getUniformInfo(const std::string &name) const override;
|
const UniformInfo *getUniformInfo(const std::string &name) const override;
|
||||||
void updateUniform(const UniformInfo *info, int count, bool internalUpdate = false) override;
|
const UniformInfo *getUniformInfo(BuiltinUniform builtin) const override;
|
||||||
void sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate = false) override;
|
void updateUniform(const UniformInfo *info, int count) override;
|
||||||
|
void sendTextures(const UniformInfo *info, Texture **textures, int count) override;
|
||||||
bool hasUniform(const std::string &name) const override;
|
bool hasUniform(const std::string &name) const override;
|
||||||
ptrdiff_t getHandle() const override;
|
ptrdiff_t getHandle() const override;
|
||||||
void setVideoTextures(ptrdiff_t ytexture, ptrdiff_t cbtexture, ptrdiff_t crtexture) override;
|
void setVideoTextures(Texture *ytexture, Texture *cbtexture, Texture *crtexture) override;
|
||||||
|
|
||||||
GLint getAttribLocation(const std::string &name);
|
GLint getAttribLocation(const std::string &name);
|
||||||
|
|
||||||
@@ -79,20 +80,23 @@ private:
|
|||||||
struct TextureUnit
|
struct TextureUnit
|
||||||
{
|
{
|
||||||
GLuint texture = 0;
|
GLuint texture = 0;
|
||||||
|
TextureType type = TEXTURE_2D;
|
||||||
bool active = false;
|
bool active = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Map active uniform names to their locations.
|
// Map active uniform names to their locations.
|
||||||
void mapActiveUniforms();
|
void mapActiveUniforms();
|
||||||
|
|
||||||
|
void updateUniform(const UniformInfo *info, int count, bool internalupdate);
|
||||||
|
void sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalupdate);
|
||||||
|
|
||||||
int getUniformTypeComponents(GLenum type) const;
|
int getUniformTypeComponents(GLenum type) const;
|
||||||
MatrixSize getMatrixSize(GLenum type) const;
|
MatrixSize getMatrixSize(GLenum type) const;
|
||||||
UniformType getUniformBaseType(GLenum type) const;
|
UniformType getUniformBaseType(GLenum type) const;
|
||||||
|
TextureType getUniformTextureType(GLenum type) const;
|
||||||
|
|
||||||
GLuint compileCode(ShaderStage stage, const std::string &code);
|
GLuint compileCode(ShaderStage stage, const std::string &code);
|
||||||
|
|
||||||
int getFreeTextureUnits(int count);
|
|
||||||
|
|
||||||
void flushStreamDraws() const;
|
void flushStreamDraws() const;
|
||||||
|
|
||||||
// Get any warnings or errors generated only by the shader program object.
|
// Get any warnings or errors generated only by the shader program object.
|
||||||
@@ -106,6 +110,7 @@ private:
|
|||||||
|
|
||||||
// Location values for any built-in uniform variables.
|
// Location values for any built-in uniform variables.
|
||||||
GLint builtinUniforms[BUILTIN_MAX_ENUM];
|
GLint builtinUniforms[BUILTIN_MAX_ENUM];
|
||||||
|
UniformInfo *builtinUniformInfo[BUILTIN_MAX_ENUM];
|
||||||
|
|
||||||
// Location values for any generic vertex attribute variables.
|
// Location values for any generic vertex attribute variables.
|
||||||
GLint builtinAttributes[ATTRIB_MAX_ENUM];
|
GLint builtinAttributes[ATTRIB_MAX_ENUM];
|
||||||
@@ -128,8 +133,6 @@ private:
|
|||||||
Matrix4 lastTransformMatrix;
|
Matrix4 lastTransformMatrix;
|
||||||
Matrix4 lastProjectionMatrix;
|
Matrix4 lastProjectionMatrix;
|
||||||
|
|
||||||
GLuint videoTextureUnits[3];
|
|
||||||
|
|
||||||
}; // Shader
|
}; // Shader
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -53,38 +53,37 @@ SpriteBatch::~SpriteBatch()
|
|||||||
|
|
||||||
void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||||
{
|
{
|
||||||
const size_t pos_offset = offsetof(Vertex, x);
|
|
||||||
const size_t texel_offset = offsetof(Vertex, s);
|
|
||||||
const size_t color_offset = offsetof(Vertex, color.r);
|
|
||||||
|
|
||||||
if (next == 0)
|
if (next == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
gfx->flushStreamDraws();
|
gfx->flushStreamDraws();
|
||||||
|
|
||||||
|
if (Shader::current && texture.get())
|
||||||
|
Shader::current->checkMainTextureType(texture->getTextureType());
|
||||||
|
|
||||||
OpenGL::TempDebugGroup debuggroup("SpriteBatch draw");
|
OpenGL::TempDebugGroup debuggroup("SpriteBatch draw");
|
||||||
|
|
||||||
Graphics::TempTransform transform(gfx, m);
|
Graphics::TempTransform transform(gfx, m);
|
||||||
|
|
||||||
gl.bindTextureToUnit(texture, 0, false);
|
gl.bindTextureToUnit(texture, 0, false);
|
||||||
|
|
||||||
uint32 enabledattribs = ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD;
|
|
||||||
|
|
||||||
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
|
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
|
||||||
array_buf->unmap();
|
array_buf->unmap();
|
||||||
|
|
||||||
gl.bindBuffer(BUFFER_VERTEX, (GLuint) array_buf->getHandle());
|
gl.bindBuffer(BUFFER_VERTEX, (GLuint) array_buf->getHandle());
|
||||||
|
|
||||||
|
uint32 enabledattribs = ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD;
|
||||||
|
|
||||||
|
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, x)));
|
||||||
|
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, s)));
|
||||||
|
|
||||||
// Apply per-sprite color, if a color is set.
|
// Apply per-sprite color, if a color is set.
|
||||||
if (color)
|
if (color)
|
||||||
{
|
{
|
||||||
enabledattribs |= ATTRIBFLAG_COLOR;
|
enabledattribs |= ATTRIBFLAG_COLOR;
|
||||||
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), BUFFER_OFFSET(color_offset));
|
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, color.r)));
|
||||||
}
|
}
|
||||||
|
|
||||||
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(pos_offset));
|
|
||||||
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(texel_offset));
|
|
||||||
|
|
||||||
for (const auto &it : attached_attributes)
|
for (const auto &it : attached_attributes)
|
||||||
{
|
{
|
||||||
Mesh *mesh = it.second.mesh.get();
|
Mesh *mesh = it.second.mesh.get();
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ void Text::draw(Graphics *gfx, const Matrix4 &m)
|
|||||||
if (vbo == nullptr || draw_commands.empty())
|
if (vbo == nullptr || draw_commands.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (Shader::current)
|
||||||
|
Shader::current->checkMainTextureType(TEXTURE_2D);
|
||||||
|
|
||||||
gfx->flushStreamDraws();
|
gfx->flushStreamDraws();
|
||||||
|
|
||||||
OpenGL::TempDebugGroup debuggroup("Text object draw");
|
OpenGL::TempDebugGroup debuggroup("Text object draw");
|
||||||
@@ -60,28 +63,24 @@ void Text::draw(Graphics *gfx, const Matrix4 &m)
|
|||||||
if ((size_t) totalverts / 4 > quadIndices.getSize())
|
if ((size_t) totalverts / 4 > quadIndices.getSize())
|
||||||
quadIndices = QuadIndices(gfx, (size_t) totalverts / 4);
|
quadIndices = QuadIndices(gfx, (size_t) totalverts / 4);
|
||||||
|
|
||||||
const size_t pos_offset = offsetof(Font::GlyphVertex, x);
|
vbo->unmap(); // Make sure all pending data is flushed to the GPU.
|
||||||
const size_t tex_offset = offsetof(Font::GlyphVertex, s);
|
|
||||||
const size_t color_offset = offsetof(Font::GlyphVertex, color.r);
|
|
||||||
const size_t stride = sizeof(Font::GlyphVertex);
|
|
||||||
|
|
||||||
const GLenum gltype = OpenGL::getGLIndexDataType(quadIndices.getType());
|
|
||||||
const size_t elemsize = quadIndices.getElementSize();
|
|
||||||
|
|
||||||
Graphics::TempTransform transform(gfx, m);
|
Graphics::TempTransform transform(gfx, m);
|
||||||
|
|
||||||
gl.prepareDraw();
|
gl.prepareDraw();
|
||||||
|
|
||||||
vbo->unmap(); // Make sure all pending data is flushed to the GPU.
|
size_t stride = sizeof(Font::GlyphVertex);
|
||||||
|
|
||||||
gl.bindBuffer(BUFFER_VERTEX, (GLuint) vbo->getHandle());
|
gl.bindBuffer(BUFFER_VERTEX, (GLuint) vbo->getHandle());
|
||||||
|
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offsetof(Font::GlyphVertex, x)));
|
||||||
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(pos_offset));
|
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, BUFFER_OFFSET(offsetof(Font::GlyphVertex, s)));
|
||||||
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, BUFFER_OFFSET(tex_offset));
|
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(offsetof(Font::GlyphVertex, color.r)));
|
||||||
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(color_offset));
|
|
||||||
|
|
||||||
gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
|
gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
|
||||||
|
|
||||||
|
const GLenum gltype = OpenGL::getGLIndexDataType(quadIndices.getType());
|
||||||
|
const size_t elemsize = quadIndices.getElementSize();
|
||||||
|
|
||||||
gl.bindBuffer(BUFFER_INDEX, (GLuint) quadIndices.getBuffer()->getHandle());
|
gl.bindBuffer(BUFFER_INDEX, (GLuint) quadIndices.getBuffer()->getHandle());
|
||||||
|
|
||||||
// We need a separate draw call for every section of the text which uses a
|
// We need a separate draw call for every section of the text which uses a
|
||||||
@@ -92,7 +91,7 @@ void Text::draw(Graphics *gfx, const Matrix4 &m)
|
|||||||
size_t offset = (cmd.startvertex / 4) * 6 * elemsize;
|
size_t offset = (cmd.startvertex / 4) * 6 * elemsize;
|
||||||
|
|
||||||
// TODO: Use glDrawElementsBaseVertex when supported?
|
// TODO: Use glDrawElementsBaseVertex when supported?
|
||||||
gl.bindTextureToUnit((GLuint) cmd.texture, 0, false);
|
gl.bindTextureToUnit(cmd.texture, 0, false);
|
||||||
|
|
||||||
gl.drawElements(GL_TRIANGLES, count, gltype, BUFFER_OFFSET(offset));
|
gl.drawElements(GL_TRIANGLES, count, gltype, BUFFER_OFFSET(offset));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,119 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2006-2017 LOVE Development Team
|
|
||||||
*
|
|
||||||
* This software is provided 'as-is', without any express or implied
|
|
||||||
* warranty. In no event will the authors be held liable for any damages
|
|
||||||
* arising from the use of this software.
|
|
||||||
*
|
|
||||||
* Permission is granted to anyone to use this software for any purpose,
|
|
||||||
* including commercial applications, and to alter it and redistribute it
|
|
||||||
* freely, subject to the following restrictions:
|
|
||||||
*
|
|
||||||
* 1. The origin of this software must not be misrepresented; you must not
|
|
||||||
* claim that you wrote the original software. If you use this software
|
|
||||||
* in a product, an acknowledgment in the product documentation would be
|
|
||||||
* appreciated but is not required.
|
|
||||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
||||||
* misrepresented as being the original software.
|
|
||||||
* 3. This notice may not be removed or altered from any source distribution.
|
|
||||||
**/
|
|
||||||
|
|
||||||
#include "Video.h"
|
|
||||||
|
|
||||||
namespace love
|
|
||||||
{
|
|
||||||
namespace graphics
|
|
||||||
{
|
|
||||||
namespace opengl
|
|
||||||
{
|
|
||||||
|
|
||||||
Video::Video(love::video::VideoStream *stream, float pixeldensity)
|
|
||||||
: love::graphics::Video(stream, pixeldensity)
|
|
||||||
{
|
|
||||||
loadVolatile();
|
|
||||||
}
|
|
||||||
|
|
||||||
Video::~Video()
|
|
||||||
{
|
|
||||||
unloadVolatile();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Video::loadVolatile()
|
|
||||||
{
|
|
||||||
GLuint textures[3];
|
|
||||||
glGenTextures(3, textures);
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++)
|
|
||||||
textureHandles[i] = textures[i];
|
|
||||||
|
|
||||||
// Create the textures using the initial frame data.
|
|
||||||
auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer();
|
|
||||||
|
|
||||||
int widths[3] = {frame->yw, frame->cw, frame->cw};
|
|
||||||
int heights[3] = {frame->yh, frame->ch, frame->ch};
|
|
||||||
|
|
||||||
const unsigned char *data[3] = {frame->yplane, frame->cbplane, frame->crplane};
|
|
||||||
|
|
||||||
Texture::Wrap wrap; // Clamp wrap mode.
|
|
||||||
|
|
||||||
bool srgb = false;
|
|
||||||
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(PIXELFORMAT_R8, false, srgb);
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
gl.bindTextureToUnit(textures[i], 0, false);
|
|
||||||
|
|
||||||
gl.setTextureFilter(filter);
|
|
||||||
gl.setTextureWrap(wrap);
|
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, fmt.internalformat, widths[i], heights[i],
|
|
||||||
0, fmt.externalformat, fmt.type, data[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Video::unloadVolatile()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
gl.deleteTexture((GLuint) textureHandles[i]);
|
|
||||||
textureHandles[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Video::uploadFrame(const love::video::VideoStream::Frame *frame)
|
|
||||||
{
|
|
||||||
int widths[3] = {frame->yw, frame->cw, frame->cw};
|
|
||||||
int heights[3] = {frame->yh, frame->ch, frame->ch};
|
|
||||||
|
|
||||||
const unsigned char *data[3] = {frame->yplane, frame->cbplane, frame->crplane};
|
|
||||||
|
|
||||||
bool srgb = false;
|
|
||||||
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(PIXELFORMAT_R8, false, srgb);
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
gl.bindTextureToUnit((GLuint) textureHandles[i], 0, false);
|
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, widths[i], heights[i],
|
|
||||||
fmt.externalformat, fmt.type, data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Video::setFilter(const Texture::Filter &f)
|
|
||||||
{
|
|
||||||
if (!Texture::validateFilter(f, false))
|
|
||||||
throw love::Exception("Invalid texture filter.");
|
|
||||||
|
|
||||||
filter = f;
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
gl.bindTextureToUnit((GLuint) textureHandles[i], 0, false);
|
|
||||||
gl.setTextureFilter(filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // opengl
|
|
||||||
} // graphics
|
|
||||||
} // love
|
|
||||||
@@ -31,18 +31,6 @@ Canvas *luax_checkcanvas(lua_State *L, int idx)
|
|||||||
return luax_checktype<Canvas>(L, idx);
|
return luax_checktype<Canvas>(L, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
int w_Canvas_getFormat(lua_State *L)
|
|
||||||
{
|
|
||||||
Canvas *canvas = luax_checkcanvas(L, 1);
|
|
||||||
PixelFormat format = canvas->getPixelFormat();
|
|
||||||
const char *str;
|
|
||||||
if (!getConstant(format, str))
|
|
||||||
return luaL_error(L, "Unknown pixel format.");
|
|
||||||
|
|
||||||
lua_pushstring(L, str);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int w_Canvas_getMSAA(lua_State *L)
|
int w_Canvas_getMSAA(lua_State *L)
|
||||||
{
|
{
|
||||||
Canvas *canvas = luax_checkcanvas(L, 1);
|
Canvas *canvas = luax_checkcanvas(L, 1);
|
||||||
@@ -52,28 +40,37 @@ int w_Canvas_getMSAA(lua_State *L)
|
|||||||
|
|
||||||
int w_Canvas_renderTo(lua_State *L)
|
int w_Canvas_renderTo(lua_State *L)
|
||||||
{
|
{
|
||||||
Canvas *canvas = luax_checkcanvas(L, 1);
|
Graphics::RenderTarget rt(luax_checkcanvas(L, 1));
|
||||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
|
||||||
|
int startidx = 2;
|
||||||
|
|
||||||
|
if (rt.canvas->getTextureType() != TEXTURE_2D)
|
||||||
|
{
|
||||||
|
rt.slice = (int) luaL_checknumber(L, 2) - 1;
|
||||||
|
startidx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
luaL_checktype(L, startidx, LUA_TFUNCTION);
|
||||||
|
|
||||||
auto graphics = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
auto graphics = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||||
|
|
||||||
if (graphics)
|
if (graphics)
|
||||||
{
|
{
|
||||||
// Save the current Canvas so we can restore it when we're done.
|
// Save the current render targets so we can restore them when we're done.
|
||||||
std::vector<Canvas *> oldcanvases = graphics->getCanvas();
|
std::vector<Graphics::RenderTarget> oldtargets = graphics->getCanvas();
|
||||||
|
|
||||||
for (Canvas *c : oldcanvases)
|
for (auto c : oldtargets)
|
||||||
c->retain();
|
c.canvas->retain();
|
||||||
|
|
||||||
luax_catchexcept(L, [&](){ graphics->setCanvas(canvas); });
|
luax_catchexcept(L, [&](){ graphics->setCanvas(rt); });
|
||||||
|
|
||||||
lua_settop(L, 2); // make sure the function is on top of the stack
|
lua_settop(L, 2); // make sure the function is on top of the stack
|
||||||
int status = lua_pcall(L, 0, 0, 0);
|
int status = lua_pcall(L, 0, 0, 0);
|
||||||
|
|
||||||
graphics->setCanvas(oldcanvases);
|
graphics->setCanvas(oldtargets);
|
||||||
|
|
||||||
for (Canvas *c : oldcanvases)
|
for (auto c : oldtargets)
|
||||||
c->release();
|
c.canvas->release();
|
||||||
|
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
return lua_error(L);
|
return lua_error(L);
|
||||||
@@ -86,13 +83,31 @@ int w_Canvas_newImageData(lua_State *L)
|
|||||||
{
|
{
|
||||||
Canvas *canvas = luax_checkcanvas(L, 1);
|
Canvas *canvas = luax_checkcanvas(L, 1);
|
||||||
love::image::Image *image = luax_getmodule<love::image::Image>(L, love::image::Image::type);
|
love::image::Image *image = luax_getmodule<love::image::Image>(L, love::image::Image::type);
|
||||||
int x = (int) luaL_optnumber(L, 2, 0);
|
|
||||||
int y = (int) luaL_optnumber(L, 3, 0);
|
int slice = 0;
|
||||||
int w = (int) luaL_optnumber(L, 4, canvas->getPixelWidth());
|
int x = 0;
|
||||||
int h = (int) luaL_optnumber(L, 5, canvas->getPixelHeight());
|
int y = 0;
|
||||||
|
int w = canvas->getPixelWidth();
|
||||||
|
int h = canvas->getPixelHeight();
|
||||||
|
|
||||||
|
int startidx = 2;
|
||||||
|
|
||||||
|
if (canvas->getTextureType() != TEXTURE_2D)
|
||||||
|
{
|
||||||
|
slice = (int) luaL_checknumber(L, startidx);
|
||||||
|
startidx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lua_isnoneornil(L, startidx))
|
||||||
|
{
|
||||||
|
x = (int) luaL_checknumber(L, startidx + 0);
|
||||||
|
y = (int) luaL_checknumber(L, startidx + 1);
|
||||||
|
w = (int) luaL_checknumber(L, startidx + 2);
|
||||||
|
h = (int) luaL_checknumber(L, startidx + 3);
|
||||||
|
}
|
||||||
|
|
||||||
love::image::ImageData *img = nullptr;
|
love::image::ImageData *img = nullptr;
|
||||||
luax_catchexcept(L, [&](){ img = canvas->newImageData(image, x, y, w, h); });
|
luax_catchexcept(L, [&](){ img = canvas->newImageData(image, slice, x, y, w, h); });
|
||||||
|
|
||||||
luax_pushtype(L, img);
|
luax_pushtype(L, img);
|
||||||
img->release();
|
img->release();
|
||||||
@@ -101,7 +116,6 @@ int w_Canvas_newImageData(lua_State *L)
|
|||||||
|
|
||||||
static const luaL_Reg w_Canvas_functions[] =
|
static const luaL_Reg w_Canvas_functions[] =
|
||||||
{
|
{
|
||||||
{ "getFormat", w_Canvas_getFormat },
|
|
||||||
{ "getMSAA", w_Canvas_getMSAA },
|
{ "getMSAA", w_Canvas_getMSAA },
|
||||||
{ "renderTo", w_Canvas_renderTo },
|
{ "renderTo", w_Canvas_renderTo },
|
||||||
{ "newImageData", w_Canvas_newImageData },
|
{ "newImageData", w_Canvas_newImageData },
|
||||||
|
|||||||
@@ -216,26 +216,67 @@ int w_setCanvas(lua_State *L)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool is_table = lua_istable(L, 1);
|
bool is_table = lua_istable(L, 1);
|
||||||
std::vector<Canvas *> canvases;
|
std::vector<Graphics::RenderTarget> targets;
|
||||||
|
|
||||||
if (is_table)
|
if (is_table)
|
||||||
{
|
{
|
||||||
|
lua_rawgeti(L, 1, 1);
|
||||||
|
bool table_of_tables = lua_istable(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
for (int i = 1; i <= (int) luax_objlen(L, 1); i++)
|
for (int i = 1; i <= (int) luax_objlen(L, 1); i++)
|
||||||
{
|
{
|
||||||
lua_rawgeti(L, 1, i);
|
lua_rawgeti(L, 1, i);
|
||||||
canvases.push_back(luax_checkcanvas(L, -1));
|
|
||||||
|
if (table_of_tables)
|
||||||
|
{
|
||||||
|
lua_rawgeti(L, -1, 1);
|
||||||
|
Graphics::RenderTarget target(luax_checkcanvas(L, -1), 0);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
TextureType type = target.canvas->getTextureType();
|
||||||
|
if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
|
||||||
|
target.slice = luax_checkintflag(L, -1, "layer") - 1;
|
||||||
|
else if (type == TEXTURE_CUBE)
|
||||||
|
target.slice = luax_checkintflag(L, -1, "face") - 1;
|
||||||
|
|
||||||
|
targets.push_back(target);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targets.emplace_back(luax_checkcanvas(L, -1), 0);
|
||||||
|
|
||||||
|
if (targets.back().canvas->getTextureType() != TEXTURE_2D)
|
||||||
|
return luaL_error(L, "The table-of-tables variant of setCanvas must be used with non-2D Canvases.");
|
||||||
|
}
|
||||||
|
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 1; i <= lua_gettop(L); i++)
|
for (int i = 1; i <= lua_gettop(L); i++)
|
||||||
canvases.push_back(luax_checkcanvas(L, i));
|
{
|
||||||
|
Graphics::RenderTarget target(luax_checkcanvas(L, i), 0);
|
||||||
|
TextureType type = target.canvas->getTextureType();
|
||||||
|
|
||||||
|
if (i == 1 && type != TEXTURE_2D)
|
||||||
|
{
|
||||||
|
target.slice = (int) luaL_checknumber(L, 2) - 1;
|
||||||
|
targets.push_back(target);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i > 1 && type != TEXTURE_2D)
|
||||||
|
return luaL_error(L, "This variant of setCanvas only supports 2D texture types.");
|
||||||
|
|
||||||
|
targets.push_back(target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
luax_catchexcept(L, [&]() {
|
luax_catchexcept(L, [&]() {
|
||||||
if (canvases.size() > 0)
|
if (targets.size() > 0)
|
||||||
instance()->setCanvas(canvases);
|
instance()->setCanvas(targets);
|
||||||
else
|
else
|
||||||
instance()->setCanvas();
|
instance()->setCanvas();
|
||||||
});
|
});
|
||||||
@@ -245,22 +286,63 @@ int w_setCanvas(lua_State *L)
|
|||||||
|
|
||||||
int w_getCanvas(lua_State *L)
|
int w_getCanvas(lua_State *L)
|
||||||
{
|
{
|
||||||
const std::vector<Canvas *> canvases = instance()->getCanvas();
|
const std::vector<Graphics::RenderTarget> targets = instance()->getCanvas();
|
||||||
int n = 0;
|
int ntargets = (int) targets.size();
|
||||||
|
|
||||||
for (Canvas *c : canvases)
|
if (ntargets == 0)
|
||||||
{
|
|
||||||
luax_pushtype(L, c);
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n == 0)
|
|
||||||
{
|
{
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
n = 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return n;
|
bool hasNon2DTextureType = false;
|
||||||
|
for (const auto &rt : targets)
|
||||||
|
{
|
||||||
|
if (rt.canvas->getTextureType() != TEXTURE_2D)
|
||||||
|
{
|
||||||
|
hasNon2DTextureType = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasNon2DTextureType)
|
||||||
|
{
|
||||||
|
lua_createtable(L, ntargets, 0);
|
||||||
|
|
||||||
|
for (int i = 0; i < ntargets; i++)
|
||||||
|
{
|
||||||
|
const auto &rt = targets[i];
|
||||||
|
|
||||||
|
lua_createtable(L, 1, 1);
|
||||||
|
|
||||||
|
luax_pushtype(L, rt.canvas);
|
||||||
|
lua_rawseti(L, -2, 1);
|
||||||
|
|
||||||
|
TextureType type = rt.canvas->getTextureType();
|
||||||
|
|
||||||
|
if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
|
||||||
|
{
|
||||||
|
lua_pushnumber(L, rt.slice + 1);
|
||||||
|
lua_setfield(L, -2, "layer");
|
||||||
|
}
|
||||||
|
else if (type == TEXTURE_VOLUME)
|
||||||
|
{
|
||||||
|
lua_pushnumber(L, rt.slice + 1);
|
||||||
|
lua_setfield(L, -2, "face");
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_rawseti(L, -2, i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (const auto &rt : targets)
|
||||||
|
luax_pushtype(L, rt.canvas);
|
||||||
|
|
||||||
|
return ntargets;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void screenshotCallback(love::image::ImageData *i, Reference *ref, void *gd)
|
static void screenshotCallback(love::image::ImageData *i, Reference *ref, void *gd)
|
||||||
@@ -413,28 +495,11 @@ int w_getStencilTest(lua_State *L)
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
int w_newImage(lua_State *L)
|
static void parsePixelDensity(love::filesystem::FileData *d, float *pixeldensity)
|
||||||
{
|
{
|
||||||
luax_checkgraphicscreated(L);
|
|
||||||
|
|
||||||
std::vector<love::image::ImageData *> data;
|
|
||||||
std::vector<love::image::CompressedImageData *> cdata;
|
|
||||||
|
|
||||||
Image::Settings settings;
|
|
||||||
|
|
||||||
bool releasedata = false;
|
|
||||||
|
|
||||||
// Convert to ImageData / CompressedImageData, if necessary.
|
|
||||||
if (lua_isstring(L, 1) || luax_istype(L, 1, love::filesystem::File::type) || luax_istype(L, 1, love::filesystem::FileData::type))
|
|
||||||
{
|
|
||||||
auto imagemodule = Module::getInstance<love::image::Image>(Module::M_IMAGE);
|
|
||||||
if (imagemodule == nullptr)
|
|
||||||
return luaL_error(L, "Cannot load images without the love.image module.");
|
|
||||||
|
|
||||||
love::filesystem::FileData *fdata = love::filesystem::luax_getfiledata(L, 1);
|
|
||||||
|
|
||||||
// Parse a density scale of 2.0 from "image@2x.png".
|
// Parse a density scale of 2.0 from "image@2x.png".
|
||||||
const std::string &fname = fdata->getName();
|
const std::string &fname = d->getName();
|
||||||
|
|
||||||
size_t namelen = fname.length();
|
size_t namelen = fname.length();
|
||||||
size_t atpos = fname.rfind('@');
|
size_t atpos = fname.rfind('@');
|
||||||
|
|
||||||
@@ -443,102 +508,314 @@ int w_newImage(lua_State *L)
|
|||||||
{
|
{
|
||||||
char *end = nullptr;
|
char *end = nullptr;
|
||||||
long density = strtol(fname.c_str() + atpos + 1, &end, 10);
|
long density = strtol(fname.c_str() + atpos + 1, &end, 10);
|
||||||
if (end != nullptr && density > 0)
|
if (end != nullptr && density > 0 && pixeldensity != nullptr)
|
||||||
settings.pixeldensity = (float) density;
|
*pixeldensity = (float) density;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Image::Settings w__optImageSettings(lua_State *L, int idx, const Image::Settings &s)
|
||||||
|
{
|
||||||
|
Image::Settings settings = s;
|
||||||
|
|
||||||
|
if (!lua_isnoneornil(L, idx))
|
||||||
|
{
|
||||||
|
luaL_checktype(L, idx, LUA_TTABLE);
|
||||||
|
settings.mipmaps = luax_boolflag(L, idx, "mipmaps", s.mipmaps);
|
||||||
|
settings.linear = luax_boolflag(L, idx, "linear", s.linear);
|
||||||
|
settings.pixeldensity = (float) luax_numberflag(L, idx, "pixeldensity", s.pixeldensity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imagemodule->isCompressed(fdata))
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::pair<StrongRef<love::image::ImageData>, StrongRef<love::image::CompressedImageData>>
|
||||||
|
getImageData(lua_State *L, int idx, bool allowcompressed, float *density)
|
||||||
|
{
|
||||||
|
StrongRef<love::image::ImageData> idata;
|
||||||
|
StrongRef<love::image::CompressedImageData> cdata;
|
||||||
|
|
||||||
|
// Convert to ImageData / CompressedImageData, if necessary.
|
||||||
|
if (lua_isstring(L, idx) || luax_istype(L, idx, love::filesystem::File::type) || luax_istype(L, idx, love::filesystem::FileData::type))
|
||||||
{
|
{
|
||||||
luax_catchexcept(L,
|
auto imagemodule = Module::getInstance<love::image::Image>(Module::M_IMAGE);
|
||||||
[&]() { cdata.push_back(imagemodule->newCompressedData(fdata)); },
|
if (imagemodule == nullptr)
|
||||||
[&](bool) { fdata->release(); }
|
luaL_error(L, "Cannot load images without the love.image module.");
|
||||||
);
|
|
||||||
}
|
StrongRef<love::filesystem::FileData> fdata(love::filesystem::luax_getfiledata(L, idx), Acquire::NORETAIN);
|
||||||
|
|
||||||
|
if (density != nullptr)
|
||||||
|
parsePixelDensity(fdata, density);
|
||||||
|
|
||||||
|
if (allowcompressed && imagemodule->isCompressed(fdata))
|
||||||
|
luax_catchexcept(L, [&]() { cdata.set(imagemodule->newCompressedData(fdata), Acquire::NORETAIN); });
|
||||||
else
|
else
|
||||||
{
|
luax_catchexcept(L, [&]() { idata.set(imagemodule->newImageData(fdata), Acquire::NORETAIN); });
|
||||||
luax_catchexcept(L,
|
|
||||||
[&]() { data.push_back(imagemodule->newImageData(fdata)); },
|
|
||||||
[&](bool) { fdata->release(); }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lua's GC won't release the image data, so we should do it ourselves.
|
|
||||||
releasedata = true;
|
|
||||||
}
|
}
|
||||||
else if (luax_istype(L, 1, love::image::CompressedImageData::type))
|
else if (luax_istype(L, idx, love::image::CompressedImageData::type))
|
||||||
cdata.push_back(love::image::luax_checkcompressedimagedata(L, 1));
|
cdata.set(love::image::luax_checkcompressedimagedata(L, idx));
|
||||||
else
|
else
|
||||||
data.push_back(love::image::luax_checkimagedata(L, 1));
|
idata.set(love::image::luax_checkimagedata(L, idx));
|
||||||
|
|
||||||
if (!lua_isnoneornil(L, 2))
|
return std::make_pair(idata, cdata);
|
||||||
{
|
}
|
||||||
luaL_checktype(L, 2, LUA_TTABLE);
|
|
||||||
|
|
||||||
settings.mipmaps = luax_boolflag(L, 2, luax_imageSettingName(Image::SETTING_MIPMAPS), settings.mipmaps);
|
static int w__pushNewImage(lua_State *L, Image::Slices &slices, const Image::Settings &settings)
|
||||||
settings.linear = luax_boolflag(L, 2, luax_imageSettingName(Image::SETTING_LINEAR), settings.linear);
|
{
|
||||||
settings.pixeldensity = (float) luax_numberflag(L, 2, luax_imageSettingName(Image::SETTING_PIXELDENSITY), settings.pixeldensity);
|
StrongRef<Image> i;
|
||||||
|
|
||||||
lua_getfield(L, 2, luax_imageSettingName(Image::SETTING_MIPMAPS));
|
|
||||||
|
|
||||||
// Add all manually specified mipmap images to the array of imagedata.
|
|
||||||
// i.e. settings = {mipmaps = {mip1, mip2, ...}}.
|
|
||||||
if (lua_istable(L, -1))
|
|
||||||
{
|
|
||||||
for (size_t i = 1; i <= luax_objlen(L, -1); i++)
|
|
||||||
{
|
|
||||||
lua_rawgeti(L, -1, i);
|
|
||||||
|
|
||||||
if (!data.empty())
|
|
||||||
{
|
|
||||||
if (!luax_istype(L, -1, love::image::ImageData::type))
|
|
||||||
luax_convobj(L, -1, "image", "newImageData");
|
|
||||||
|
|
||||||
data.push_back(love::image::luax_checkimagedata(L, -1));
|
|
||||||
}
|
|
||||||
else if (!cdata.empty())
|
|
||||||
{
|
|
||||||
if (!luax_istype(L, -1, love::image::CompressedImageData::type))
|
|
||||||
luax_convobj(L, -1, "image", "newCompressedData");
|
|
||||||
|
|
||||||
cdata.push_back(love::image::luax_checkcompressedimagedata(L, -1));
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the image.
|
|
||||||
Image *image = nullptr;
|
|
||||||
luax_catchexcept(L,
|
luax_catchexcept(L,
|
||||||
[&]() {
|
[&]() { i.set(instance()->newImage(slices, settings), Acquire::NORETAIN); },
|
||||||
if (!cdata.empty())
|
[&](bool) { slices.clear(); }
|
||||||
image = instance()->newImage(cdata, settings);
|
|
||||||
else if (!data.empty())
|
|
||||||
image = instance()->newImage(data, settings);
|
|
||||||
},
|
|
||||||
[&](bool) {
|
|
||||||
if (releasedata)
|
|
||||||
{
|
|
||||||
for (auto d : data)
|
|
||||||
d->release();
|
|
||||||
for (auto d : cdata)
|
|
||||||
d->release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (image == nullptr)
|
luax_pushtype(L, i);
|
||||||
return luaL_error(L, "Could not load image.");
|
|
||||||
|
|
||||||
// Push the type.
|
|
||||||
luax_pushtype(L, image);
|
|
||||||
image->release();
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int w_newCubeImage(lua_State *L)
|
||||||
|
{
|
||||||
|
luax_checkgraphicscreated(L);
|
||||||
|
|
||||||
|
Image::Slices slices(TEXTURE_CUBE);
|
||||||
|
Image::Settings settings;
|
||||||
|
|
||||||
|
auto imagemodule = Module::getInstance<love::image::Image>(Module::M_IMAGE);
|
||||||
|
|
||||||
|
if (!lua_istable(L, 1))
|
||||||
|
{
|
||||||
|
auto imagedata = getImageData(L, 1, false, &settings.pixeldensity);
|
||||||
|
|
||||||
|
std::vector<StrongRef<love::image::ImageData>> faces;
|
||||||
|
luax_catchexcept(L, [&](){ faces = imagemodule->newCubeFaces(imagedata.first); });
|
||||||
|
|
||||||
|
for (int i = 0; i < (int) faces.size(); i++)
|
||||||
|
slices.set(i, 0, faces[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int tlen = (int) luax_objlen(L, 1);
|
||||||
|
|
||||||
|
if (luax_isarrayoftables(L, 1))
|
||||||
|
{
|
||||||
|
if (tlen != 6)
|
||||||
|
return luaL_error(L, "Cubemap images must have 6 faces.");
|
||||||
|
|
||||||
|
for (int face = 0; face < tlen; face++)
|
||||||
|
{
|
||||||
|
lua_rawgeti(L, 1, face + 1);
|
||||||
|
luaL_checktype(L, -1, LUA_TTABLE);
|
||||||
|
|
||||||
|
int miplen = std::max(1, (int) luax_objlen(L, -1));
|
||||||
|
|
||||||
|
for (int mip = 0; mip < miplen; mip++)
|
||||||
|
{
|
||||||
|
lua_rawgeti(L, -1, mip + 1);
|
||||||
|
|
||||||
|
auto data = getImageData(L, -1, true, face == 0 && mip == 0 ? &settings.pixeldensity : nullptr);
|
||||||
|
if (data.first.get())
|
||||||
|
slices.set(face, mip, data.first);
|
||||||
|
else
|
||||||
|
slices.set(face, mip, data.second->getSlice(0, 0));
|
||||||
|
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool usemipmaps = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < tlen; i++)
|
||||||
|
{
|
||||||
|
lua_rawgeti(L, 1, i + 1);
|
||||||
|
|
||||||
|
auto data = getImageData(L, -1, true, i == 0 ? &settings.pixeldensity : nullptr);
|
||||||
|
|
||||||
|
if (data.first.get())
|
||||||
|
{
|
||||||
|
if (usemipmaps || data.first->getWidth() != data.first->getHeight())
|
||||||
|
{
|
||||||
|
usemipmaps = true;
|
||||||
|
|
||||||
|
std::vector<StrongRef<love::image::ImageData>> faces;
|
||||||
|
luax_catchexcept(L, [&](){ faces = imagemodule->newCubeFaces(data.first); });
|
||||||
|
|
||||||
|
for (int face = 0; face < (int) faces.size(); face++)
|
||||||
|
slices.set(face, i, faces[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
slices.set(i, 0, data.first);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
slices.add(data.second, i, 0, false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pop(L, tlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
settings = w__optImageSettings(L, 2, settings);
|
||||||
|
return w__pushNewImage(L, slices, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
int w_newArrayImage(lua_State *L)
|
||||||
|
{
|
||||||
|
luax_checkgraphicscreated(L);
|
||||||
|
|
||||||
|
Image::Slices slices(TEXTURE_2D_ARRAY);
|
||||||
|
Image::Settings settings;
|
||||||
|
|
||||||
|
if (lua_istable(L, 1))
|
||||||
|
{
|
||||||
|
int tlen = std::max(1, (int) luax_objlen(L, 1));
|
||||||
|
|
||||||
|
if (luax_isarrayoftables(L, 1))
|
||||||
|
{
|
||||||
|
for (int slice = 0; slice < tlen; slice++)
|
||||||
|
{
|
||||||
|
lua_rawgeti(L, 1, slice + 1);
|
||||||
|
luaL_checktype(L, -1, LUA_TTABLE);
|
||||||
|
|
||||||
|
int miplen = std::max(1, (int) luax_objlen(L, -1));
|
||||||
|
|
||||||
|
for (int mip = 0; mip < miplen; mip++)
|
||||||
|
{
|
||||||
|
lua_rawgeti(L, -1, mip + 1);
|
||||||
|
|
||||||
|
auto data = getImageData(L, -1, true, slice == 0 && mip == 0 ? &settings.pixeldensity : nullptr);
|
||||||
|
if (data.first.get())
|
||||||
|
slices.set(slice, mip, data.first);
|
||||||
|
else
|
||||||
|
slices.set(slice, mip, data.second->getSlice(0, 0));
|
||||||
|
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int slice = 0; slice < tlen; slice++)
|
||||||
|
{
|
||||||
|
lua_rawgeti(L, 1, slice + 1);
|
||||||
|
auto data = getImageData(L, -1, true, slice == 0 ? &settings.pixeldensity : nullptr);
|
||||||
|
if (data.first.get())
|
||||||
|
slices.set(slice, 0, data.first);
|
||||||
|
else
|
||||||
|
slices.add(data.second, slice, 0, false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pop(L, tlen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto data = getImageData(L, 1, true, &settings.pixeldensity);
|
||||||
|
if (data.first.get())
|
||||||
|
slices.set(0, 0, data.first);
|
||||||
|
else
|
||||||
|
slices.add(data.second, 0, 0, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
settings = w__optImageSettings(L, 2, settings);
|
||||||
|
return w__pushNewImage(L, slices, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
int w_newVolumeImage(lua_State *L)
|
||||||
|
{
|
||||||
|
luax_checkgraphicscreated(L);
|
||||||
|
|
||||||
|
Image::Slices slices(TEXTURE_VOLUME);
|
||||||
|
Image::Settings settings;
|
||||||
|
|
||||||
|
if (lua_istable(L, 1))
|
||||||
|
{
|
||||||
|
int tlen = std::max(1, (int) luax_objlen(L, 1));
|
||||||
|
|
||||||
|
if (luax_isarrayoftables(L, 1))
|
||||||
|
{
|
||||||
|
for (int mip = 0; mip < tlen; mip++)
|
||||||
|
{
|
||||||
|
lua_rawgeti(L, 1, mip + 1);
|
||||||
|
luaL_checktype(L, -1, LUA_TTABLE);
|
||||||
|
|
||||||
|
int slicelen = std::max(1, (int) luax_objlen(L, -1));
|
||||||
|
|
||||||
|
for (int slice = 0; slice < slicelen; slice++)
|
||||||
|
{
|
||||||
|
lua_rawgeti(L, -1, mip + 1);
|
||||||
|
|
||||||
|
auto data = getImageData(L, -1, true, slice == 0 && mip == 0 ? &settings.pixeldensity : nullptr);
|
||||||
|
if (data.first.get())
|
||||||
|
slices.set(slice, mip, data.first);
|
||||||
|
else
|
||||||
|
slices.set(slice, mip, data.second->getSlice(0, 0));
|
||||||
|
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int layer = 0; layer < tlen; layer++)
|
||||||
|
{
|
||||||
|
lua_rawgeti(L, 1, layer + 1);
|
||||||
|
auto data = getImageData(L, -1, true, layer == 0 ? &settings.pixeldensity : nullptr);
|
||||||
|
if (data.first.get())
|
||||||
|
slices.set(layer, 0, data.first);
|
||||||
|
else
|
||||||
|
slices.add(data.second, layer, 0, false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pop(L, tlen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto data = getImageData(L, 1, true, &settings.pixeldensity);
|
||||||
|
if (data.first.get())
|
||||||
|
slices.set(0, 0, data.first);
|
||||||
|
else
|
||||||
|
slices.add(data.second, 0, 0, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
settings = w__optImageSettings(L, 2, settings);
|
||||||
|
return w__pushNewImage(L, slices, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
int w_newImage(lua_State *L)
|
||||||
|
{
|
||||||
|
luax_checkgraphicscreated(L);
|
||||||
|
|
||||||
|
Image::Slices slices(TEXTURE_2D);
|
||||||
|
Image::Settings settings;
|
||||||
|
|
||||||
|
if (lua_istable(L, 1))
|
||||||
|
{
|
||||||
|
int n = std::max(1, (int) luax_objlen(L, 1));
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
lua_rawgeti(L, 1, i + 1);
|
||||||
|
auto data = getImageData(L, -1, true, i == 0 ? &settings.pixeldensity : nullptr);
|
||||||
|
if (data.first.get())
|
||||||
|
slices.set(0, i, data.first);
|
||||||
|
else
|
||||||
|
slices.set(0, i, data.second->getSlice(0, 0));
|
||||||
|
}
|
||||||
|
lua_pop(L, n);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto data = getImageData(L, 1, true, &settings.pixeldensity);
|
||||||
|
if (data.first.get())
|
||||||
|
slices.set(0, 0, data.first);
|
||||||
|
else
|
||||||
|
slices.add(data.second, 0, 0, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
settings = w__optImageSettings(L, 2, settings);
|
||||||
|
return w__pushNewImage(L, slices, settings);
|
||||||
|
}
|
||||||
|
|
||||||
int w_newQuad(lua_State *L)
|
int w_newQuad(lua_State *L)
|
||||||
{
|
{
|
||||||
luax_checkgraphicscreated(L);
|
luax_checkgraphicscreated(L);
|
||||||
@@ -605,18 +882,6 @@ int w_newImageFont(lua_State *L)
|
|||||||
// filter for glyphs
|
// filter for glyphs
|
||||||
Texture::Filter filter = instance()->getDefaultFilter();
|
Texture::Filter filter = instance()->getDefaultFilter();
|
||||||
|
|
||||||
// Convert to ImageData if necessary.
|
|
||||||
if (luax_istype(L, 1, Image::type))
|
|
||||||
{
|
|
||||||
Image *i = luax_checktype<Image>(L, 1);
|
|
||||||
filter = i->getFilter();
|
|
||||||
const auto &idlevels = i->getImageData();
|
|
||||||
if (idlevels.empty())
|
|
||||||
return luaL_argerror(L, 1, "Image must not be compressed.");
|
|
||||||
luax_pushtype(L, idlevels[0].get());
|
|
||||||
lua_replace(L, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to Rasterizer if necessary.
|
// Convert to Rasterizer if necessary.
|
||||||
if (!luax_istype(L, 1, love::font::Rasterizer::type))
|
if (!luax_istype(L, 1, love::font::Rasterizer::type))
|
||||||
{
|
{
|
||||||
@@ -687,35 +952,50 @@ int w_newCanvas(lua_State *L)
|
|||||||
{
|
{
|
||||||
luax_checkgraphicscreated(L);
|
luax_checkgraphicscreated(L);
|
||||||
|
|
||||||
// check if width and height are given. else default to screen dimensions.
|
|
||||||
int width = (int) luaL_optnumber(L, 1, instance()->getWidth());
|
|
||||||
int height = (int) luaL_optnumber(L, 2, instance()->getHeight());
|
|
||||||
|
|
||||||
Canvas::Settings settings;
|
Canvas::Settings settings;
|
||||||
|
|
||||||
|
// check if width and height are given. else default to screen dimensions.
|
||||||
|
settings.width = (int) luaL_optnumber(L, 1, instance()->getWidth());
|
||||||
|
settings.height = (int) luaL_optnumber(L, 2, instance()->getHeight());
|
||||||
|
|
||||||
// Default to the screen's current pixel density scale.
|
// Default to the screen's current pixel density scale.
|
||||||
settings.pixeldensity = instance()->getScreenPixelDensity();
|
settings.pixeldensity = instance()->getScreenPixelDensity();
|
||||||
|
|
||||||
if (!lua_isnoneornil(L, 3))
|
int startidx = 3;
|
||||||
|
|
||||||
|
if (lua_isnumber(L, 3))
|
||||||
{
|
{
|
||||||
lua_getfield(L, 3, "format");
|
settings.layers = (int) luaL_checknumber(L, 3);
|
||||||
|
settings.type = TEXTURE_2D_ARRAY;
|
||||||
|
startidx = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lua_isnoneornil(L, startidx))
|
||||||
|
{
|
||||||
|
settings.pixeldensity = (float) luax_numberflag(L, startidx, "pixeldensity", settings.pixeldensity);
|
||||||
|
settings.msaa = luax_intflag(L, startidx, "msaa", 0);
|
||||||
|
|
||||||
|
lua_getfield(L, startidx, "format");
|
||||||
if (!lua_isnoneornil(L, -1))
|
if (!lua_isnoneornil(L, -1))
|
||||||
{
|
{
|
||||||
const char *str = luaL_checkstring(L, -1);
|
const char *str = luaL_checkstring(L, -1);
|
||||||
if (!getConstant(str, settings.format))
|
if (!getConstant(str, settings.format))
|
||||||
return luaL_error(L, "Invalid Canvas format: %s", str);
|
return luaL_error(L, "Invalid pixel format: %s", str);
|
||||||
}
|
}
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
settings.pixeldensity = (float) luax_numberflag(L, 3, "pixeldensity", settings.pixeldensity);
|
lua_getfield(L, startidx, "type");
|
||||||
settings.msaa = luax_intflag(L, 3, "msaa", settings.msaa);
|
if (!lua_isnoneornil(L, -1))
|
||||||
|
{
|
||||||
|
const char *str = luaL_checkstring(L, -1);
|
||||||
|
if (!Texture::getConstant(str, settings.type))
|
||||||
|
return luaL_error(L, "Invalid texture type: %s", str);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Canvas *canvas = nullptr;
|
Canvas *canvas = nullptr;
|
||||||
luax_catchexcept(L, [&](){ canvas = instance()->newCanvas(width, height, settings); });
|
luax_catchexcept(L, [&](){ canvas = instance()->newCanvas(settings); });
|
||||||
|
|
||||||
if (canvas == nullptr)
|
|
||||||
return luaL_error(L, "Canvas not created, but no error thrown. I don't even...");
|
|
||||||
|
|
||||||
luax_pushtype(L, canvas);
|
luax_pushtype(L, canvas);
|
||||||
canvas->release();
|
canvas->release();
|
||||||
@@ -2178,6 +2458,9 @@ static const luaL_Reg functions[] =
|
|||||||
{ "present", w_present },
|
{ "present", w_present },
|
||||||
|
|
||||||
{ "newImage", w_newImage },
|
{ "newImage", w_newImage },
|
||||||
|
{ "newArrayImage", w_newArrayImage },
|
||||||
|
{ "newVolumeImage", w_newVolumeImage },
|
||||||
|
{ "newCubeImage", w_newCubeImage },
|
||||||
{ "newQuad", w_newQuad },
|
{ "newQuad", w_newQuad },
|
||||||
{ "newFont", w_newFont },
|
{ "newFont", w_newFont },
|
||||||
{ "newImageFont", w_newImageFont },
|
{ "newImageFont", w_newImageFont },
|
||||||
|
|||||||
@@ -42,7 +42,17 @@ GLSL.SYNTAX = [[
|
|||||||
#endif
|
#endif
|
||||||
#define number float
|
#define number float
|
||||||
#define Image sampler2D
|
#define Image sampler2D
|
||||||
#define extern uniform]]
|
#define ArrayImage sampler2DArray
|
||||||
|
#define CubeImage samplerCube
|
||||||
|
#define VolumeImage sampler3D
|
||||||
|
#define extern uniform
|
||||||
|
#ifdef GL_EXT_texture_array
|
||||||
|
#extension GL_EXT_texture_array : enable
|
||||||
|
#endif
|
||||||
|
#ifdef GL_OES_texture_3D
|
||||||
|
#extension GL_OES_texture_3D : enable
|
||||||
|
#endif
|
||||||
|
]]
|
||||||
|
|
||||||
-- Uniforms shared by the vertex and pixel shader stages.
|
-- Uniforms shared by the vertex and pixel shader stages.
|
||||||
GLSL.UNIFORMS = [[
|
GLSL.UNIFORMS = [[
|
||||||
@@ -66,13 +76,36 @@ GLSL.FUNCTIONS = [[
|
|||||||
#else
|
#else
|
||||||
#if __VERSION__ >= 130
|
#if __VERSION__ >= 130
|
||||||
#define texture2D Texel
|
#define texture2D Texel
|
||||||
|
#define texture3D Texel
|
||||||
|
#define textureCube Texel
|
||||||
|
#define texture2DArray Texel
|
||||||
#define love_texture2D texture
|
#define love_texture2D texture
|
||||||
|
#define love_texture3D texture
|
||||||
|
#define love_textureCube texture
|
||||||
|
#define love_texture2DArray texture
|
||||||
#else
|
#else
|
||||||
#define love_texture2D texture2D
|
#define love_texture2D texture2D
|
||||||
|
#define love_texture3D texture3D
|
||||||
|
#define love_textureCube textureCube
|
||||||
|
#define love_texture2DArray texture2DArray
|
||||||
#endif
|
#endif
|
||||||
vec4 Texel(sampler2D s, vec2 c) { return love_texture2D(s, c); }
|
vec4 Texel(sampler2D s, vec2 c) { return love_texture2D(s, c); }
|
||||||
|
vec4 Texel(samplerCube s, vec3 c) { return love_textureCube(s, c); }
|
||||||
|
#if __VERSION__ > 100 || defined(GL_OES_texture_3D)
|
||||||
|
vec4 Texel(sampler3D s, vec3 c) { return love_texture3D(s, c); }
|
||||||
|
#endif
|
||||||
|
#if __VERSION__ >= 130 || defined(GL_EXT_texture_array)
|
||||||
|
vec4 Texel(sampler2DArray s, vec3 c) { return love_texture2DArray(s, c); }
|
||||||
|
#endif
|
||||||
#ifdef PIXEL
|
#ifdef PIXEL
|
||||||
vec4 Texel(sampler2D s, vec2 c, float b) { return love_texture2D(s, c, b); }
|
vec4 Texel(sampler2D s, vec2 c, float b) { return love_texture2D(s, c, b); }
|
||||||
|
vec4 Texel(samplerCube s, vec3 c, float b) { return love_textureCube(s, c, b); }
|
||||||
|
#if __VERSION__ > 100 || defined(GL_OES_texture_3D)
|
||||||
|
vec4 Texel(sampler3D s, vec3 c, float b) { return love_texture3D(s, c, b); }
|
||||||
|
#endif
|
||||||
|
#if __VERSION__ >= 130 || defined(GL_EXT_texture_array)
|
||||||
|
vec4 Texel(sampler2DArray s, vec3 c, float b) { return love_texture2DArray(s, c, b); }
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#define texture love_texture
|
#define texture love_texture
|
||||||
#endif
|
#endif
|
||||||
@@ -187,17 +220,11 @@ GLSL.PIXEL = {
|
|||||||
|
|
||||||
#if __VERSION__ >= 130
|
#if __VERSION__ >= 130
|
||||||
#define varying in
|
#define varying in
|
||||||
#ifdef LOVE_MULTI_CANVAS
|
|
||||||
layout(location = 0) out vec4 love_Canvases[love_MaxCanvases];
|
layout(location = 0) out vec4 love_Canvases[love_MaxCanvases];
|
||||||
#else
|
#define love_PixelColor love_Canvases[0]
|
||||||
layout(location = 0) out vec4 love_PixelColor;
|
|
||||||
#endif
|
|
||||||
#else
|
#else
|
||||||
#ifdef LOVE_MULTI_CANVAS
|
|
||||||
#define love_Canvases gl_FragData
|
#define love_Canvases gl_FragData
|
||||||
#else
|
|
||||||
#define love_PixelColor gl_FragColor
|
#define love_PixelColor gl_FragColor
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// See Shader::updateScreenParams in Shader.cpp.
|
// See Shader::updateScreenParams in Shader.cpp.
|
||||||
@@ -259,7 +286,6 @@ local function createShaderStageCode(stage, code, lang, gles, glsl1on3, gammacor
|
|||||||
"#define "..stage,
|
"#define "..stage,
|
||||||
glsl1on3 and "#define LOVE_GLSL1_ON_GLSL3 1" or "",
|
glsl1on3 and "#define LOVE_GLSL1_ON_GLSL3 1" or "",
|
||||||
gammacorrect and "#define LOVE_GAMMA_CORRECT 1" or "",
|
gammacorrect and "#define LOVE_GAMMA_CORRECT 1" or "",
|
||||||
multicanvas and "#define LOVE_MULTI_CANVAS 1" or "",
|
|
||||||
GLSL.SYNTAX,
|
GLSL.SYNTAX,
|
||||||
GLSL[stage].HEADER,
|
GLSL[stage].HEADER,
|
||||||
GLSL.UNIFORMS,
|
GLSL.UNIFORMS,
|
||||||
|
|||||||
@@ -31,40 +31,11 @@ Image *luax_checkimage(lua_State *L, int idx)
|
|||||||
return luax_checktype<Image>(L, idx);
|
return luax_checktype<Image>(L, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
int w_Image_setMipmapFilter(lua_State *L)
|
int w_Image_isFormatLinear(lua_State *L)
|
||||||
{
|
{
|
||||||
Image *t = luax_checkimage(L, 1);
|
Image *i = luax_checkimage(L, 1);
|
||||||
Texture::Filter f = t->getFilter();
|
luax_pushboolean(L, i->isFormatLinear());
|
||||||
|
return 1;
|
||||||
if (lua_isnoneornil(L, 2))
|
|
||||||
f.mipmap = Texture::FILTER_NONE; // mipmapping is disabled if no argument is given
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const char *mipmapstr = luaL_checkstring(L, 2);
|
|
||||||
if (!Texture::getConstant(mipmapstr, f.mipmap))
|
|
||||||
return luaL_error(L, "Invalid filter mode: %s", mipmapstr);
|
|
||||||
}
|
|
||||||
|
|
||||||
luax_catchexcept(L, [&](){ t->setFilter(f); });
|
|
||||||
t->setMipmapSharpness((float) luaL_optnumber(L, 3, 0.0));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int w_Image_getMipmapFilter(lua_State *L)
|
|
||||||
{
|
|
||||||
Image *t = luax_checkimage(L, 1);
|
|
||||||
|
|
||||||
const Texture::Filter &f = t->getFilter();
|
|
||||||
|
|
||||||
const char *mipmapstr;
|
|
||||||
if (Texture::getConstant(f.mipmap, mipmapstr))
|
|
||||||
lua_pushstring(L, mipmapstr);
|
|
||||||
else
|
|
||||||
lua_pushnil(L); // only return a mipmap filter if mipmapping is enabled
|
|
||||||
|
|
||||||
lua_pushnumber(L, t->getMipmapSharpness());
|
|
||||||
return 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int w_Image_isCompressed(lua_State *L)
|
int w_Image_isCompressed(lua_State *L)
|
||||||
@@ -74,78 +45,33 @@ int w_Image_isCompressed(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int w_Image_refresh(lua_State *L)
|
int w_Image_replacePixels(lua_State *L)
|
||||||
{
|
{
|
||||||
Image *i = luax_checkimage(L, 1);
|
Image *i = luax_checkimage(L, 1);
|
||||||
|
love::image::ImageData *id = luax_checktype<love::image::ImageData>(L, 2);
|
||||||
|
|
||||||
int xoffset = (int) luaL_optnumber(L, 2, 0);
|
int slice = 0;
|
||||||
int yoffset = (int) luaL_optnumber(L, 3, 0);
|
int mipmap = 0;
|
||||||
int w = (int) luaL_optnumber(L, 4, i->getWidth());
|
bool reloadmipmaps = i->getMipmapsType() == Image::MIPMAPS_GENERATED;
|
||||||
int h = (int) luaL_optnumber(L, 5, i->getHeight());
|
|
||||||
|
|
||||||
luax_catchexcept(L, [&](){ i->refresh(xoffset, yoffset, w, h); });
|
if (i->getTextureType() != TEXTURE_2D)
|
||||||
|
{
|
||||||
|
slice = (int) luaL_checknumber(L, 3) - 1;
|
||||||
|
if (!reloadmipmaps)
|
||||||
|
mipmap = (int) luaL_optnumber(L, 4, 1) - 1;
|
||||||
|
}
|
||||||
|
else if (!reloadmipmaps)
|
||||||
|
mipmap = (int) luaL_optnumber(L, 3, 1) - 1;
|
||||||
|
|
||||||
|
luax_catchexcept(L, [&](){ i->replacePixels(id, slice, mipmap, reloadmipmaps); });
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int w_Image_getData(lua_State *L)
|
|
||||||
{
|
|
||||||
Image *i = luax_checkimage(L, 1);
|
|
||||||
int n = 0;
|
|
||||||
|
|
||||||
if (i->isCompressed())
|
|
||||||
{
|
|
||||||
for (const auto &cdata : i->getCompressedData())
|
|
||||||
{
|
|
||||||
luax_pushtype(L, cdata.get());
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (const auto &data : i->getImageData())
|
|
||||||
{
|
|
||||||
luax_pushtype(L, data.get());
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *luax_imageSettingName(Image::SettingType settingtype)
|
|
||||||
{
|
|
||||||
const char *name = nullptr;
|
|
||||||
Image::getConstant(settingtype, name);
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
int w_Image_getFlags(lua_State *L)
|
|
||||||
{
|
|
||||||
Image *i = luax_checkimage(L, 1);
|
|
||||||
Image::Settings settings = i->getFlags();
|
|
||||||
|
|
||||||
lua_createtable(L, 0, 2);
|
|
||||||
|
|
||||||
lua_pushboolean(L, settings.mipmaps);
|
|
||||||
lua_setfield(L, -2, luax_imageSettingName(Image::SETTING_MIPMAPS));
|
|
||||||
|
|
||||||
lua_pushboolean(L, settings.linear);
|
|
||||||
lua_setfield(L, -2, luax_imageSettingName(Image::SETTING_LINEAR));
|
|
||||||
|
|
||||||
lua_pushnumber(L, settings.pixeldensity);
|
|
||||||
lua_setfield(L, -2, luax_imageSettingName(Image::SETTING_PIXELDENSITY));
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const luaL_Reg w_Image_functions[] =
|
static const luaL_Reg w_Image_functions[] =
|
||||||
{
|
{
|
||||||
{ "setMipmapFilter", w_Image_setMipmapFilter },
|
{ "isFormatLinear", w_Image_isFormatLinear },
|
||||||
{ "getMipmapFilter", w_Image_getMipmapFilter },
|
|
||||||
{ "isCompressed", w_Image_isCompressed },
|
{ "isCompressed", w_Image_isCompressed },
|
||||||
{ "refresh", w_Image_refresh },
|
{ "replacePixels", w_Image_replacePixels },
|
||||||
{ "getData", w_Image_getData },
|
|
||||||
{ "getFlags", w_Image_getFlags },
|
|
||||||
{ 0, 0 }
|
{ 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ namespace love
|
|||||||
namespace graphics
|
namespace graphics
|
||||||
{
|
{
|
||||||
|
|
||||||
const char *luax_imageSettingName(Image::SettingType settingtype);
|
|
||||||
Image *luax_checkimage(lua_State *L, int idx);
|
Image *luax_checkimage(lua_State *L, int idx);
|
||||||
extern "C" int luaopen_image(lua_State *L);
|
extern "C" int luaopen_image(lua_State *L);
|
||||||
|
|
||||||
|
|||||||
@@ -456,7 +456,7 @@ int w_Mesh_setTexture(lua_State *L)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Texture *tex = luax_checktexture(L, 2);
|
Texture *tex = luax_checktexture(L, 2);
|
||||||
t->setTexture(tex);
|
luax_catchexcept(L, [&](){ t->setTexture(tex); });
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ int w_ParticleSystem_setTexture(lua_State *L)
|
|||||||
{
|
{
|
||||||
ParticleSystem *t = luax_checkparticlesystem(L, 1);
|
ParticleSystem *t = luax_checkparticlesystem(L, 1);
|
||||||
Texture *tex = luax_checktexture(L, 2);
|
Texture *tex = luax_checktexture(L, 2);
|
||||||
t->setTexture(tex);
|
luax_catchexcept(L, [&](){ t->setTexture(tex); });
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -267,7 +267,12 @@ int w_Shader_sendTextures(lua_State *L, int startidx, Shader *shader, const Shad
|
|||||||
textures.reserve(count);
|
textures.reserve(count);
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
textures.push_back(luax_checktexture(L, startidx + i));
|
{
|
||||||
|
Texture *tex = luax_checktexture(L, startidx + i);
|
||||||
|
if (tex->getTextureType() != info->textureType)
|
||||||
|
return luaL_argerror(L, startidx + i, "invalid texture type for uniform");
|
||||||
|
textures.push_back(tex);
|
||||||
|
}
|
||||||
|
|
||||||
luax_catchexcept(L, [&]() { shader->sendTextures(info, textures.data(), count); });
|
luax_catchexcept(L, [&]() { shader->sendTextures(info, textures.data(), count); });
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ int w_SpriteBatch_setTexture(lua_State *L)
|
|||||||
{
|
{
|
||||||
SpriteBatch *t = luax_checkspritebatch(L, 1);
|
SpriteBatch *t = luax_checkspritebatch(L, 1);
|
||||||
Texture *tex = luax_checktexture(L, 2);
|
Texture *tex = luax_checktexture(L, 2);
|
||||||
t->setTexture(tex);
|
luax_catchexcept(L, [&](){ t->setTexture(tex); });
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,16 @@ Texture *luax_checktexture(lua_State *L, int idx)
|
|||||||
return luax_checktype<Texture>(L, idx);
|
return luax_checktype<Texture>(L, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int w_Texture_getTextureType(lua_State *L)
|
||||||
|
{
|
||||||
|
Texture *t = luax_checktexture(L, 1);
|
||||||
|
const char *tstr;
|
||||||
|
if (!Texture::getConstant(t->getTextureType(), tstr))
|
||||||
|
return luaL_error(L, "unknown texture type");
|
||||||
|
lua_pushstring(L, tstr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int w_Texture_getWidth(lua_State *L)
|
int w_Texture_getWidth(lua_State *L)
|
||||||
{
|
{
|
||||||
Texture *t = luax_checktexture(L, 1);
|
Texture *t = luax_checktexture(L, 1);
|
||||||
@@ -52,6 +62,27 @@ int w_Texture_getDimensions(lua_State *L)
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int w_Texture_getDepth(lua_State *L)
|
||||||
|
{
|
||||||
|
Texture *t = luax_checktexture(L, 1);
|
||||||
|
lua_pushnumber(L, t->getDepth());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int w_Texture_getLayerCount(lua_State *L)
|
||||||
|
{
|
||||||
|
Texture *t = luax_checktexture(L, 1);
|
||||||
|
lua_pushnumber(L, t->getLayerCount());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int w_Texture_getMipmapCount(lua_State *L)
|
||||||
|
{
|
||||||
|
Texture *t = luax_checktexture(L, 1);
|
||||||
|
lua_pushnumber(L, t->getMipmapCount());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int w_Texture_getPixelWidth(lua_State *L)
|
int w_Texture_getPixelWidth(lua_State *L)
|
||||||
{
|
{
|
||||||
Texture *t = luax_checktexture(L, 1);
|
Texture *t = luax_checktexture(L, 1);
|
||||||
@@ -119,6 +150,42 @@ int w_Texture_getFilter(lua_State *L)
|
|||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int w_Texture_setMipmapFilter(lua_State *L)
|
||||||
|
{
|
||||||
|
Texture *t = luax_checktexture(L, 1);
|
||||||
|
Texture::Filter f = t->getFilter();
|
||||||
|
|
||||||
|
if (lua_isnoneornil(L, 2))
|
||||||
|
f.mipmap = Texture::FILTER_NONE; // mipmapping is disabled if no argument is given
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char *mipmapstr = luaL_checkstring(L, 2);
|
||||||
|
if (!Texture::getConstant(mipmapstr, f.mipmap))
|
||||||
|
return luaL_error(L, "Invalid filter mode: %s", mipmapstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
luax_catchexcept(L, [&](){ t->setFilter(f); });
|
||||||
|
t->setMipmapSharpness((float) luaL_optnumber(L, 3, 0.0));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int w_Texture_getMipmapFilter(lua_State *L)
|
||||||
|
{
|
||||||
|
Texture *t = luax_checktexture(L, 1);
|
||||||
|
|
||||||
|
const Texture::Filter &f = t->getFilter();
|
||||||
|
|
||||||
|
const char *mipmapstr;
|
||||||
|
if (Texture::getConstant(f.mipmap, mipmapstr))
|
||||||
|
lua_pushstring(L, mipmapstr);
|
||||||
|
else
|
||||||
|
lua_pushnil(L); // only return a mipmap filter if mipmapping is enabled
|
||||||
|
|
||||||
|
lua_pushnumber(L, t->getMipmapSharpness());
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
int w_Texture_setWrap(lua_State *L)
|
int w_Texture_setWrap(lua_State *L)
|
||||||
{
|
{
|
||||||
Texture *t = luax_checktexture(L, 1);
|
Texture *t = luax_checktexture(L, 1);
|
||||||
@@ -143,30 +210,53 @@ int w_Texture_getWrap(lua_State *L)
|
|||||||
|
|
||||||
const char *sstr = nullptr;
|
const char *sstr = nullptr;
|
||||||
const char *tstr = nullptr;
|
const char *tstr = nullptr;
|
||||||
|
const char *rstr = nullptr;
|
||||||
|
|
||||||
if (!Texture::getConstant(w.s, sstr))
|
if (!Texture::getConstant(w.s, sstr))
|
||||||
return luaL_error(L, "Unknown wrap mode.");
|
return luaL_error(L, "Unknown wrap mode.");
|
||||||
if (!Texture::getConstant(w.t, tstr))
|
if (!Texture::getConstant(w.t, tstr))
|
||||||
return luaL_error(L, "Unknown wrap mode.");
|
return luaL_error(L, "Unknown wrap mode.");
|
||||||
|
if (!Texture::getConstant(w.r, rstr))
|
||||||
|
return luaL_error(L, "Unknown wrap mode.");
|
||||||
|
|
||||||
lua_pushstring(L, sstr);
|
lua_pushstring(L, sstr);
|
||||||
lua_pushstring(L, tstr);
|
lua_pushstring(L, tstr);
|
||||||
|
lua_pushstring(L, rstr);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int w_Texture_getFormat(lua_State *L)
|
||||||
|
{
|
||||||
|
Texture *t = luax_checktexture(L, 1);
|
||||||
|
PixelFormat format = t->getPixelFormat();
|
||||||
|
const char *str;
|
||||||
|
if (!getConstant(format, str))
|
||||||
|
return luaL_error(L, "Unknown pixel format.");
|
||||||
|
|
||||||
|
lua_pushstring(L, str);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
const luaL_Reg w_Texture_functions[] =
|
const luaL_Reg w_Texture_functions[] =
|
||||||
{
|
{
|
||||||
|
{ "getTextureType", w_Texture_getTextureType },
|
||||||
{ "getWidth", w_Texture_getWidth },
|
{ "getWidth", w_Texture_getWidth },
|
||||||
{ "getHeight", w_Texture_getHeight },
|
{ "getHeight", w_Texture_getHeight },
|
||||||
{ "getDimensions", w_Texture_getDimensions },
|
{ "getDimensions", w_Texture_getDimensions },
|
||||||
|
{ "getDepth", w_Texture_getDepth },
|
||||||
|
{ "getLayerCount", w_Texture_getLayerCount },
|
||||||
|
{ "getMipmapCount", w_Texture_getMipmapCount },
|
||||||
{ "getPixelWidth", w_Texture_getPixelWidth },
|
{ "getPixelWidth", w_Texture_getPixelWidth },
|
||||||
{ "getPixelHeight", w_Texture_getPixelHeight },
|
{ "getPixelHeight", w_Texture_getPixelHeight },
|
||||||
{ "getPixelDimensions", w_Texture_getPixelDimensions },
|
{ "getPixelDimensions", w_Texture_getPixelDimensions },
|
||||||
{ "getPixelDensity", w_Texture_getPixelDensity },
|
{ "getPixelDensity", w_Texture_getPixelDensity },
|
||||||
{ "setFilter", w_Texture_setFilter },
|
{ "setFilter", w_Texture_setFilter },
|
||||||
{ "getFilter", w_Texture_getFilter },
|
{ "getFilter", w_Texture_getFilter },
|
||||||
|
{ "setMipmapFilter", w_Texture_setMipmapFilter },
|
||||||
|
{ "getMipmapFilter", w_Texture_getMipmapFilter },
|
||||||
{ "setWrap", w_Texture_setWrap },
|
{ "setWrap", w_Texture_setWrap },
|
||||||
{ "getWrap", w_Texture_getWrap },
|
{ "getWrap", w_Texture_getWrap },
|
||||||
|
{ "getFormat", w_Texture_getFormat },
|
||||||
{ 0, 0 }
|
{ 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -27,11 +27,57 @@ namespace image
|
|||||||
|
|
||||||
love::Type CompressedImageData::type("CompressedImageData", &Data::type);
|
love::Type CompressedImageData::type("CompressedImageData", &Data::type);
|
||||||
|
|
||||||
|
CompressedImageData::Memory::Memory(size_t size)
|
||||||
|
: data(nullptr)
|
||||||
|
, size(size)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
data = new uint8[size];
|
||||||
|
}
|
||||||
|
catch (std::exception &)
|
||||||
|
{
|
||||||
|
throw love::Exception("Out of memory.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CompressedImageData::Memory::~Memory()
|
||||||
|
{
|
||||||
|
delete[] data;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompressedImageData::Slice::Slice(PixelFormat format, int width, int height, Memory *memory, size_t offset, size_t size)
|
||||||
|
: memory(memory)
|
||||||
|
, offset(offset)
|
||||||
|
, dataSize(size)
|
||||||
|
{
|
||||||
|
this->format = format;
|
||||||
|
this->width = width;
|
||||||
|
this->height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompressedImageData::Slice::Slice(const Slice &s)
|
||||||
|
: memory(s.memory)
|
||||||
|
, offset(s.offset)
|
||||||
|
, dataSize(s.dataSize)
|
||||||
|
{
|
||||||
|
this->format = s.getFormat();
|
||||||
|
this->width = s.getWidth();
|
||||||
|
this->height = s.getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
CompressedImageData::Slice::~Slice()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CompressedImageData::Slice *CompressedImageData::Slice::clone() const
|
||||||
|
{
|
||||||
|
return new Slice(*this);
|
||||||
|
}
|
||||||
|
|
||||||
CompressedImageData::CompressedImageData()
|
CompressedImageData::CompressedImageData()
|
||||||
: format(PIXELFORMAT_UNKNOWN)
|
: format(PIXELFORMAT_UNKNOWN)
|
||||||
, sRGB(false)
|
, sRGB(false)
|
||||||
, data(nullptr)
|
|
||||||
, dataSize(0)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,45 +87,50 @@ CompressedImageData::~CompressedImageData()
|
|||||||
|
|
||||||
size_t CompressedImageData::getSize() const
|
size_t CompressedImageData::getSize() const
|
||||||
{
|
{
|
||||||
return dataSize;
|
return memory->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *CompressedImageData::getData() const
|
void *CompressedImageData::getData() const
|
||||||
{
|
{
|
||||||
return data;
|
return memory->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CompressedImageData::getMipmapCount() const
|
int CompressedImageData::getMipmapCount(int /*slice*/) const
|
||||||
{
|
{
|
||||||
return (int) dataImages.size();
|
return (int) dataImages.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CompressedImageData::getSliceCount(int /*mip*/) const
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
size_t CompressedImageData::getSize(int miplevel) const
|
size_t CompressedImageData::getSize(int miplevel) const
|
||||||
{
|
{
|
||||||
checkMipmapLevelExists(miplevel);
|
checkSliceExists(0, miplevel);
|
||||||
|
|
||||||
return dataImages[miplevel].size;
|
return dataImages[miplevel]->getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void *CompressedImageData::getData(int miplevel) const
|
void *CompressedImageData::getData(int miplevel) const
|
||||||
{
|
{
|
||||||
checkMipmapLevelExists(miplevel);
|
checkSliceExists(0, miplevel);
|
||||||
|
|
||||||
return &dataImages[miplevel].data[0];
|
return dataImages[miplevel]->getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
int CompressedImageData::getWidth(int miplevel) const
|
int CompressedImageData::getWidth(int miplevel) const
|
||||||
{
|
{
|
||||||
checkMipmapLevelExists(miplevel);
|
checkSliceExists(0, miplevel);
|
||||||
|
|
||||||
return dataImages[miplevel].width;
|
return dataImages[miplevel]->getWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
int CompressedImageData::getHeight(int miplevel) const
|
int CompressedImageData::getHeight(int miplevel) const
|
||||||
{
|
{
|
||||||
checkMipmapLevelExists(miplevel);
|
checkSliceExists(0, miplevel);
|
||||||
|
|
||||||
return dataImages[miplevel].height;
|
return dataImages[miplevel]->getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
PixelFormat CompressedImageData::getFormat() const
|
PixelFormat CompressedImageData::getFormat() const
|
||||||
@@ -92,8 +143,18 @@ bool CompressedImageData::isSRGB() const
|
|||||||
return sRGB;
|
return sRGB;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompressedImageData::checkMipmapLevelExists(int miplevel) const
|
CompressedImageData::Slice *CompressedImageData::getSlice(int slice, int miplevel) const
|
||||||
{
|
{
|
||||||
|
checkSliceExists(slice, miplevel);
|
||||||
|
|
||||||
|
return dataImages[miplevel].get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompressedImageData::checkSliceExists(int slice, int miplevel) const
|
||||||
|
{
|
||||||
|
if (slice != 0)
|
||||||
|
throw love::Exception("Slice index %d does not exists", slice + 1);
|
||||||
|
|
||||||
if (miplevel < 0 || miplevel >= (int) dataImages.size())
|
if (miplevel < 0 || miplevel >= (int) dataImages.size())
|
||||||
throw love::Exception("Mipmap level %d does not exist", miplevel + 1);
|
throw love::Exception("Mipmap level %d does not exist", miplevel + 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,14 @@
|
|||||||
* 3. This notice may not be removed or altered from any source distribution.
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
#ifndef LOVE_IMAGE_COMPRESSED_IMAGE_DATA_H
|
#pragma once
|
||||||
#define LOVE_IMAGE_COMPRESSED_IMAGE_DATA_H
|
|
||||||
|
|
||||||
// LOVE
|
// LOVE
|
||||||
#include "common/Data.h"
|
#include "common/Data.h"
|
||||||
#include "common/StringMap.h"
|
#include "common/StringMap.h"
|
||||||
#include "common/int.h"
|
#include "common/int.h"
|
||||||
#include "common/pixelformat.h"
|
#include "common/pixelformat.h"
|
||||||
|
#include "ImageDataBase.h"
|
||||||
|
|
||||||
// STL
|
// STL
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -44,16 +44,45 @@ class CompressedImageData : public Data
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
static love::Type type;
|
class Memory : public Object
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
Memory(size_t size);
|
||||||
|
virtual ~Memory();
|
||||||
|
|
||||||
|
uint8 *data;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
}; // Memory
|
||||||
|
|
||||||
// Compressed image data can have multiple mipmap levels, each represented
|
// Compressed image data can have multiple mipmap levels, each represented
|
||||||
// by a sub-image.
|
// by a sub-image.
|
||||||
struct SubImage
|
class Slice : public ImageDataBase
|
||||||
{
|
{
|
||||||
int width, height;
|
public:
|
||||||
size_t size;
|
|
||||||
uint8 *data; // Should not have ownership of the data.
|
Slice(PixelFormat format, int width, int height, Memory *memory, size_t offset, size_t size);
|
||||||
};
|
Slice(const Slice &slice);
|
||||||
|
virtual ~Slice();
|
||||||
|
|
||||||
|
Slice *clone() const override;
|
||||||
|
void *getData() const override { return memory->data + offset; }
|
||||||
|
size_t getSize() const override { return dataSize; }
|
||||||
|
bool isSRGB() const override { return sRGB; }
|
||||||
|
size_t getOffset() const { return offset; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
StrongRef<Memory> memory;
|
||||||
|
|
||||||
|
size_t offset;
|
||||||
|
size_t dataSize;
|
||||||
|
bool sRGB;
|
||||||
|
|
||||||
|
}; // Slice
|
||||||
|
|
||||||
|
static love::Type type;
|
||||||
|
|
||||||
CompressedImageData();
|
CompressedImageData();
|
||||||
virtual ~CompressedImageData();
|
virtual ~CompressedImageData();
|
||||||
@@ -67,7 +96,12 @@ public:
|
|||||||
* Gets the number of mipmaps in this Compressed Image Data.
|
* Gets the number of mipmaps in this Compressed Image Data.
|
||||||
* Includes the base image level.
|
* Includes the base image level.
|
||||||
**/
|
**/
|
||||||
int getMipmapCount() const;
|
int getMipmapCount(int slice = 0) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of slices (array layers, cube faces, 3D layers, etc.)
|
||||||
|
**/
|
||||||
|
int getSliceCount(int mip = 0) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the size in bytes of a sub-image at the specified mipmap level.
|
* Gets the size in bytes of a sub-image at the specified mipmap level.
|
||||||
@@ -96,6 +130,8 @@ public:
|
|||||||
|
|
||||||
bool isSRGB() const;
|
bool isSRGB() const;
|
||||||
|
|
||||||
|
Slice *getSlice(int slice, int miplevel) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
PixelFormat format;
|
PixelFormat format;
|
||||||
@@ -103,17 +139,14 @@ protected:
|
|||||||
bool sRGB;
|
bool sRGB;
|
||||||
|
|
||||||
// Single block of memory containing all of the sub-images.
|
// Single block of memory containing all of the sub-images.
|
||||||
uint8 *data;
|
StrongRef<Memory> memory;
|
||||||
size_t dataSize;
|
|
||||||
|
|
||||||
// Texture info for each mipmap level.
|
// Texture info for each mipmap level.
|
||||||
std::vector<SubImage> dataImages;
|
std::vector<StrongRef<Slice>> dataImages;
|
||||||
|
|
||||||
void checkMipmapLevelExists(int miplevel) const;
|
void checkSliceExists(int slice, int miplevel) const;
|
||||||
|
|
||||||
}; // CompressedImageData
|
}; // CompressedImageData
|
||||||
|
|
||||||
} // image
|
} // image
|
||||||
} // love
|
} // love
|
||||||
|
|
||||||
#endif // LOVE_IMAGE_COMPRESSED_IMAGE_DATA_H
|
|
||||||
|
|||||||
@@ -28,5 +28,92 @@ namespace image
|
|||||||
|
|
||||||
love::Type Image::type("image", &Module::type);
|
love::Type Image::type("image", &Module::type);
|
||||||
|
|
||||||
|
ImageData *Image::newPastedImageData(ImageData *src, int sx, int sy, int w, int h)
|
||||||
|
{
|
||||||
|
ImageData *res = newImageData(w, h, src->getFormat());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
res->paste(src, 0, 0, sx, sy, w, h);
|
||||||
|
}
|
||||||
|
catch (love::Exception &)
|
||||||
|
{
|
||||||
|
res->release();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<StrongRef<ImageData>> Image::newCubeFaces(love::image::ImageData *src)
|
||||||
|
{
|
||||||
|
// The faces array is always ordered +x, -x, +y, -y, +z, -z.
|
||||||
|
std::vector<StrongRef<ImageData>> faces;
|
||||||
|
|
||||||
|
int totalW = src->getWidth();
|
||||||
|
int totalH = src->getHeight();
|
||||||
|
|
||||||
|
if (totalW % 3 == 0 && totalH % 4 == 0 && totalW / 3 == totalH / 4)
|
||||||
|
{
|
||||||
|
// +y
|
||||||
|
// +z +x -z
|
||||||
|
// -y
|
||||||
|
// -x
|
||||||
|
|
||||||
|
int w = totalW / 3;
|
||||||
|
int h = totalH / 4;
|
||||||
|
|
||||||
|
faces.emplace_back(newPastedImageData(src, 1*w, 1*h, w, h), Acquire::NORETAIN);
|
||||||
|
faces.emplace_back(newPastedImageData(src, 1*w, 3*h, w, h), Acquire::NORETAIN);
|
||||||
|
faces.emplace_back(newPastedImageData(src, 1*w, 0*h, w, h), Acquire::NORETAIN);
|
||||||
|
faces.emplace_back(newPastedImageData(src, 1*w, 2*h, w, h), Acquire::NORETAIN);
|
||||||
|
faces.emplace_back(newPastedImageData(src, 0*w, 1*h, w, h), Acquire::NORETAIN);
|
||||||
|
faces.emplace_back(newPastedImageData(src, 2*w, 1*h, w, h), Acquire::NORETAIN);
|
||||||
|
}
|
||||||
|
else if (totalW % 4 == 0 && totalH % 3 == 0 && totalW / 4 == totalH / 3)
|
||||||
|
{
|
||||||
|
// +y
|
||||||
|
// -x +z +x -z
|
||||||
|
// -y
|
||||||
|
|
||||||
|
int w = totalW / 4;
|
||||||
|
int h = totalH / 3;
|
||||||
|
|
||||||
|
faces.emplace_back(newPastedImageData(src, 2*w, 1*h, w, h), Acquire::NORETAIN);
|
||||||
|
faces.emplace_back(newPastedImageData(src, 0*w, 1*h, w, h), Acquire::NORETAIN);
|
||||||
|
faces.emplace_back(newPastedImageData(src, 1*w, 0*h, w, h), Acquire::NORETAIN);
|
||||||
|
faces.emplace_back(newPastedImageData(src, 1*w, 2*h, w, h), Acquire::NORETAIN);
|
||||||
|
faces.emplace_back(newPastedImageData(src, 1*w, 1*h, w, h), Acquire::NORETAIN);
|
||||||
|
faces.emplace_back(newPastedImageData(src, 3*w, 1*h, w, h), Acquire::NORETAIN);
|
||||||
|
}
|
||||||
|
else if (totalH % 6 == 0 && totalW == totalH / 6)
|
||||||
|
{
|
||||||
|
// +x
|
||||||
|
// -x
|
||||||
|
// +y
|
||||||
|
// -y
|
||||||
|
// +z
|
||||||
|
// -z
|
||||||
|
|
||||||
|
int w = totalW;
|
||||||
|
int h = totalH / 6;
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
faces.emplace_back(newPastedImageData(src, 0, i * h, w, h), Acquire::NORETAIN);
|
||||||
|
}
|
||||||
|
else if (totalW % 6 == 0 && totalW / 6 == totalH)
|
||||||
|
{
|
||||||
|
// +x -x +y -y +z -z
|
||||||
|
|
||||||
|
int w = totalW / 6;
|
||||||
|
int h = totalH;
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
faces.emplace_back(newPastedImageData(src, i * w, 0, w, h), Acquire::NORETAIN);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw love::Exception("Unknown cubemap image dimensions!");
|
||||||
|
|
||||||
|
return faces;
|
||||||
|
}
|
||||||
|
|
||||||
} // image
|
} // image
|
||||||
} // love
|
} // love
|
||||||
|
|||||||
@@ -90,6 +90,12 @@ public:
|
|||||||
**/
|
**/
|
||||||
virtual bool isCompressed(love::filesystem::FileData *data) = 0;
|
virtual bool isCompressed(love::filesystem::FileData *data) = 0;
|
||||||
|
|
||||||
|
std::vector<StrongRef<ImageData>> newCubeFaces(ImageData *layoutData);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
ImageData *newPastedImageData(ImageData *src, int sx, int sy, int w, int h);
|
||||||
|
|
||||||
}; // Image
|
}; // Image
|
||||||
|
|
||||||
} // image
|
} // image
|
||||||
|
|||||||
@@ -30,10 +30,7 @@ namespace image
|
|||||||
love::Type ImageData::type("ImageData", &Data::type);
|
love::Type ImageData::type("ImageData", &Data::type);
|
||||||
|
|
||||||
ImageData::ImageData()
|
ImageData::ImageData()
|
||||||
: format(PIXELFORMAT_UNKNOWN)
|
: data(nullptr)
|
||||||
, width(0)
|
|
||||||
, height(0)
|
|
||||||
, data(nullptr)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,26 +48,16 @@ void *ImageData::getData() const
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ImageData::isSRGB() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool ImageData::inside(int x, int y) const
|
bool ImageData::inside(int x, int y) const
|
||||||
{
|
{
|
||||||
return x >= 0 && x < getWidth() && y >= 0 && y < getHeight();
|
return x >= 0 && x < getWidth() && y >= 0 && y < getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
PixelFormat ImageData::getFormat() const
|
|
||||||
{
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ImageData::getWidth() const
|
|
||||||
{
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ImageData::getHeight() const
|
|
||||||
{
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImageData::setPixel(int x, int y, const Pixel &p)
|
void ImageData::setPixel(int x, int y, const Pixel &p)
|
||||||
{
|
{
|
||||||
if (!inside(x, y))
|
if (!inside(x, y))
|
||||||
|
|||||||
@@ -18,8 +18,7 @@
|
|||||||
* 3. This notice may not be removed or altered from any source distribution.
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
#ifndef LOVE_IMAGE_IMAGE_DATA_H
|
#pragma once
|
||||||
#define LOVE_IMAGE_IMAGE_DATA_H
|
|
||||||
|
|
||||||
// LOVE
|
// LOVE
|
||||||
#include "common/Data.h"
|
#include "common/Data.h"
|
||||||
@@ -29,6 +28,7 @@
|
|||||||
#include "common/halffloat.h"
|
#include "common/halffloat.h"
|
||||||
#include "filesystem/FileData.h"
|
#include "filesystem/FileData.h"
|
||||||
#include "thread/threads.h"
|
#include "thread/threads.h"
|
||||||
|
#include "ImageDataBase.h"
|
||||||
|
|
||||||
using love::thread::Mutex;
|
using love::thread::Mutex;
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ union Pixel
|
|||||||
/**
|
/**
|
||||||
* Represents raw pixel data.
|
* Represents raw pixel data.
|
||||||
**/
|
**/
|
||||||
class ImageData : public Data
|
class ImageData : public ImageDataBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -71,8 +71,6 @@ public:
|
|||||||
ImageData();
|
ImageData();
|
||||||
virtual ~ImageData();
|
virtual ~ImageData();
|
||||||
|
|
||||||
PixelFormat getFormat() const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Paste part of one ImageData onto another. The subregion defined by the top-left
|
* Paste part of one ImageData onto another. The subregion defined by the top-left
|
||||||
* corner (sx, sy) and the size (sw,sh) will be pasted to (dx,dy) in this ImageData.
|
* corner (sx, sy) and the size (sw,sh) will be pasted to (dx,dy) in this ImageData.
|
||||||
@@ -92,16 +90,6 @@ public:
|
|||||||
**/
|
**/
|
||||||
bool inside(int x, int y) const;
|
bool inside(int x, int y) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the width of this ImageData.
|
|
||||||
**/
|
|
||||||
int getWidth() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the height of this ImageData.
|
|
||||||
**/
|
|
||||||
int getHeight() const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the pixel at location (x,y).
|
* Sets the pixel at location (x,y).
|
||||||
* @param x The location along the x-axis.
|
* @param x The location along the x-axis.
|
||||||
@@ -127,10 +115,11 @@ public:
|
|||||||
|
|
||||||
love::thread::Mutex *getMutex() const;
|
love::thread::Mutex *getMutex() const;
|
||||||
|
|
||||||
// Implements Data.
|
// Implements ImageDataBase.
|
||||||
virtual ImageData *clone() const = 0;
|
virtual ImageData *clone() const override = 0;
|
||||||
virtual void *getData() const;
|
void *getData() const override;
|
||||||
virtual size_t getSize() const;
|
size_t getSize() const override;
|
||||||
|
bool isSRGB() const override;
|
||||||
|
|
||||||
size_t getPixelSize() const;
|
size_t getPixelSize() const;
|
||||||
|
|
||||||
@@ -141,14 +130,6 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
PixelFormat format;
|
|
||||||
|
|
||||||
// The width of the image data.
|
|
||||||
int width;
|
|
||||||
|
|
||||||
// The height of the image data.
|
|
||||||
int height;
|
|
||||||
|
|
||||||
// The actual data.
|
// The actual data.
|
||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
|
|
||||||
@@ -166,5 +147,3 @@ private:
|
|||||||
|
|
||||||
} // image
|
} // image
|
||||||
} // love
|
} // love
|
||||||
|
|
||||||
#endif // LOVE_IMAGE_IMAGE_DATA_H
|
|
||||||
|
|||||||
@@ -18,45 +18,34 @@
|
|||||||
* 3. This notice may not be removed or altered from any source distribution.
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
#pragma once
|
#include "ImageDataBase.h"
|
||||||
|
|
||||||
// LOVE
|
|
||||||
#include "graphics/Font.h"
|
|
||||||
#include "graphics/Volatile.h"
|
|
||||||
#include "OpenGL.h"
|
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
namespace graphics
|
namespace image
|
||||||
{
|
|
||||||
namespace opengl
|
|
||||||
{
|
{
|
||||||
|
|
||||||
class Font final : public love::graphics::Font, public Volatile
|
ImageDataBase::ImageDataBase()
|
||||||
|
: format(PIXELFORMAT_UNKNOWN)
|
||||||
|
, width(0)
|
||||||
|
, height(0)
|
||||||
{
|
{
|
||||||
public:
|
}
|
||||||
|
|
||||||
Font(love::font::Rasterizer *r, const Texture::Filter &filter);
|
PixelFormat ImageDataBase::getFormat() const
|
||||||
virtual ~Font();
|
{
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
void setFilter(const Texture::Filter &f) override;
|
int ImageDataBase::getWidth() const
|
||||||
|
{
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
// Implements Volatile.
|
int ImageDataBase::getHeight() const
|
||||||
bool loadVolatile() override;
|
{
|
||||||
void unloadVolatile() override;
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
} // image
|
||||||
|
|
||||||
void createTexture() override;
|
|
||||||
void uploadGlyphToTexture(font::GlyphData *data, Glyph &glyph) override;
|
|
||||||
|
|
||||||
// vector of packed textures
|
|
||||||
std::vector<GLuint> textures;
|
|
||||||
|
|
||||||
size_t textureMemorySize;
|
|
||||||
|
|
||||||
}; // Font
|
|
||||||
|
|
||||||
} // opengl
|
|
||||||
} // graphics
|
|
||||||
} // love
|
} // love
|
||||||
@@ -21,38 +21,35 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// LOVE
|
// LOVE
|
||||||
#include "graphics/Video.h"
|
#include "common/Data.h"
|
||||||
#include "graphics/Volatile.h"
|
#include "common/pixelformat.h"
|
||||||
#include "OpenGL.h"
|
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
namespace graphics
|
namespace image
|
||||||
{
|
|
||||||
namespace opengl
|
|
||||||
{
|
{
|
||||||
|
|
||||||
class Video : public love::graphics::Video, public Volatile
|
class ImageDataBase : public Data
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Video(love::video::VideoStream *stream, float pixeldensity = 1.0f);
|
ImageDataBase();
|
||||||
virtual ~Video();
|
virtual ~ImageDataBase() {}
|
||||||
|
|
||||||
// Volatile
|
PixelFormat getFormat() const;
|
||||||
bool loadVolatile() override;
|
|
||||||
void unloadVolatile() override;
|
|
||||||
|
|
||||||
void setFilter(const Texture::Filter &f) override;
|
int getWidth() const;
|
||||||
|
int getHeight() const;
|
||||||
|
|
||||||
private:
|
virtual bool isSRGB() const = 0;
|
||||||
|
|
||||||
void uploadFrame(const love::video::VideoStream::Frame *frame) override;
|
protected:
|
||||||
|
|
||||||
Texture::Filter filter;
|
PixelFormat format;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
|
||||||
}; // Video
|
}; // ImageDataBase
|
||||||
|
|
||||||
} // opengl
|
} // image
|
||||||
} // graphics
|
|
||||||
} // love
|
} // love
|
||||||
@@ -104,7 +104,7 @@ bool ASTCHandler::canParse(const filesystem::FileData *data)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 *ASTCHandler::parse(filesystem::FileData *filedata, std::vector<CompressedImageData::SubImage> &images, size_t &dataSize, PixelFormat &format, bool &sRGB)
|
StrongRef<CompressedImageData::Memory> ASTCHandler::parse(filesystem::FileData *filedata, std::vector<StrongRef<CompressedImageData::Slice>> &images, PixelFormat &format, bool &sRGB)
|
||||||
{
|
{
|
||||||
if (!canParse(filedata))
|
if (!canParse(filedata))
|
||||||
throw love::Exception("Could not decode compressed data (not an .astc file?)");
|
throw love::Exception("Could not decode compressed data (not an .astc file?)");
|
||||||
@@ -129,35 +129,17 @@ uint8 *ASTCHandler::parse(filesystem::FileData *filedata, std::vector<Compressed
|
|||||||
if (totalsize + sizeof(header) > filedata->getSize())
|
if (totalsize + sizeof(header) > filedata->getSize())
|
||||||
throw love::Exception("Could not parse .astc file: file is too small.");
|
throw love::Exception("Could not parse .astc file: file is too small.");
|
||||||
|
|
||||||
uint8 *data = nullptr;
|
StrongRef<CompressedImageData::Memory> memory(new CompressedImageData::Memory(totalsize), Acquire::NORETAIN);
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
data = new uint8[totalsize];
|
|
||||||
}
|
|
||||||
catch (std::bad_alloc &)
|
|
||||||
{
|
|
||||||
throw love::Exception("Out of memory.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// .astc files only store a single mipmap level.
|
// .astc files only store a single mipmap level.
|
||||||
memcpy(data, (uint8 *) filedata->getData() + sizeof(ASTCHeader), totalsize);
|
memcpy(memory->data, (uint8 *) filedata->getData() + sizeof(ASTCHeader), totalsize);
|
||||||
|
|
||||||
CompressedImageData::SubImage mip;
|
images.emplace_back(new CompressedImageData::Slice(cformat, sizeX, sizeY, memory, 0, totalsize), Acquire::NORETAIN);
|
||||||
|
|
||||||
mip.width = sizeX;
|
|
||||||
mip.height = sizeY;
|
|
||||||
|
|
||||||
mip.size = totalsize;
|
|
||||||
mip.data = data;
|
|
||||||
|
|
||||||
images.push_back(mip);
|
|
||||||
|
|
||||||
dataSize = totalsize;
|
|
||||||
format = cformat;
|
format = cformat;
|
||||||
sRGB = false;
|
sRGB = false;
|
||||||
|
|
||||||
return data;
|
return memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // magpie
|
} // magpie
|
||||||
|
|||||||
@@ -42,8 +42,11 @@ public:
|
|||||||
virtual ~ASTCHandler() {}
|
virtual ~ASTCHandler() {}
|
||||||
|
|
||||||
// Implements CompressedFormatHandler.
|
// Implements CompressedFormatHandler.
|
||||||
virtual bool canParse(const filesystem::FileData *data);
|
bool canParse(const filesystem::FileData *data) override;
|
||||||
virtual uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedImageData::SubImage> &images, size_t &dataSize, PixelFormat &format, bool &sRGB);
|
|
||||||
|
StrongRef<CompressedImageData::Memory> parse(filesystem::FileData *filedata,
|
||||||
|
std::vector<StrongRef<CompressedImageData::Slice>> &images,
|
||||||
|
PixelFormat &format, bool &sRGB) override;
|
||||||
|
|
||||||
}; // ASTCHandler
|
}; // ASTCHandler
|
||||||
|
|
||||||
|
|||||||
@@ -58,14 +58,14 @@ public:
|
|||||||
* @param[in] filedata The data to parse.
|
* @param[in] filedata The data to parse.
|
||||||
* @param[out] images The list of sub-images generated. Byte data is a pointer
|
* @param[out] images The list of sub-images generated. Byte data is a pointer
|
||||||
* to the returned data.
|
* to the returned data.
|
||||||
* @param[out] dataSize The total size in bytes of the returned data.
|
|
||||||
* @param[out] format The format of the Compressed Data.
|
* @param[out] format The format of the Compressed Data.
|
||||||
* @param[out] sRGB Whether the texture is sRGB-encoded.
|
* @param[out] sRGB Whether the texture is sRGB-encoded.
|
||||||
*
|
*
|
||||||
* @return The single block of memory containing the parsed images.
|
* @return The single block of memory containing the parsed images.
|
||||||
**/
|
**/
|
||||||
virtual uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedImageData::SubImage> &images,
|
virtual StrongRef<CompressedImageData::Memory> parse(filesystem::FileData *filedata,
|
||||||
size_t &dataSize, PixelFormat &format, bool &sRGB) = 0;
|
std::vector<StrongRef<CompressedImageData::Slice>> &images,
|
||||||
|
PixelFormat &format, bool &sRGB) = 0;
|
||||||
|
|
||||||
}; // CompressedFormatHandler
|
}; // CompressedFormatHandler
|
||||||
|
|
||||||
|
|||||||
@@ -43,42 +43,31 @@ CompressedImageData::CompressedImageData(std::list<CompressedFormatHandler *> fo
|
|||||||
if (parser == nullptr)
|
if (parser == nullptr)
|
||||||
throw love::Exception("Could not parse compressed data: Unknown format.");
|
throw love::Exception("Could not parse compressed data: Unknown format.");
|
||||||
|
|
||||||
data = parser->parse(filedata, dataImages, dataSize, format, sRGB);
|
memory = parser->parse(filedata, dataImages, format, sRGB);
|
||||||
|
|
||||||
if (data == nullptr)
|
if (memory == nullptr)
|
||||||
throw love::Exception("Could not parse compressed data.");
|
throw love::Exception("Could not parse compressed data.");
|
||||||
|
|
||||||
if (format == PIXELFORMAT_UNKNOWN)
|
if (format == PIXELFORMAT_UNKNOWN)
|
||||||
{
|
|
||||||
delete[] data;
|
|
||||||
throw love::Exception("Could not parse compressed data: Unknown format.");
|
throw love::Exception("Could not parse compressed data: Unknown format.");
|
||||||
}
|
|
||||||
|
|
||||||
if (dataImages.size() == 0 || dataSize == 0)
|
if (dataImages.size() == 0 || memory->size == 0)
|
||||||
{
|
|
||||||
delete[] data;
|
|
||||||
throw love::Exception("Could not parse compressed data: No valid data?");
|
throw love::Exception("Could not parse compressed data: No valid data?");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CompressedImageData::CompressedImageData(const CompressedImageData &c)
|
CompressedImageData::CompressedImageData(const CompressedImageData &c)
|
||||||
{
|
{
|
||||||
format = c.format;
|
format = c.format;
|
||||||
sRGB = c.sRGB;
|
sRGB = c.sRGB;
|
||||||
dataSize = c.dataSize;
|
|
||||||
|
|
||||||
data = new uint8[dataSize];
|
memory.set(new Memory(c.memory->size), Acquire::NORETAIN);
|
||||||
memcpy(data, c.data, dataSize);
|
memcpy(memory->data, c.memory->data, memory->size);
|
||||||
|
|
||||||
for (auto i : c.dataImages )
|
for (const auto &i : c.dataImages)
|
||||||
{
|
{
|
||||||
struct SubImage s;
|
Slice *slice = new Slice(i->getFormat(), i->getWidth(), i->getHeight(), memory, i->getOffset(), i->getSize());
|
||||||
s.width = i.width;
|
dataImages.push_back(slice);
|
||||||
s.height = i.height;
|
slice->release();
|
||||||
s.size = i.size;
|
|
||||||
s.data = (uint8*)((intptr_t)data + (intptr_t)i.data - (intptr_t)c.data);
|
|
||||||
|
|
||||||
dataImages.push_back(s);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +78,6 @@ CompressedImageData *CompressedImageData::clone() const
|
|||||||
|
|
||||||
CompressedImageData::~CompressedImageData()
|
CompressedImageData::~CompressedImageData()
|
||||||
{
|
{
|
||||||
delete[] data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // magpie
|
} // magpie
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ ImageData::~ImageData()
|
|||||||
handler->release();
|
handler->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageData *ImageData::clone() const
|
love::image::ImageData *ImageData::clone() const
|
||||||
{
|
{
|
||||||
return new ImageData(*this);
|
return new ImageData(*this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ public:
|
|||||||
ImageData(const ImageData &c);
|
ImageData(const ImageData &c);
|
||||||
virtual ~ImageData();
|
virtual ~ImageData();
|
||||||
|
|
||||||
virtual ImageData *clone() const;
|
|
||||||
// Implements image::ImageData.
|
// Implements image::ImageData.
|
||||||
|
virtual love::image::ImageData *clone() const;
|
||||||
virtual love::filesystem::FileData *encode(EncodedFormat encodedFormat, const char *filename);
|
virtual love::filesystem::FileData *encode(EncodedFormat encodedFormat, const char *filename);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ bool KTXHandler::canParse(const filesystem::FileData *data)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 *KTXHandler::parse(filesystem::FileData *filedata, std::vector<CompressedImageData::SubImage> &images, size_t &dataSize, PixelFormat &format, bool &sRGB)
|
StrongRef<CompressedImageData::Memory> KTXHandler::parse(filesystem::FileData *filedata, std::vector<StrongRef<CompressedImageData::Slice>> &images, PixelFormat &format, bool &sRGB)
|
||||||
{
|
{
|
||||||
if (!canParse(filedata))
|
if (!canParse(filedata))
|
||||||
throw love::Exception("Could not decode compressed data (not a KTX file?)");
|
throw love::Exception("Could not decode compressed data (not a KTX file?)");
|
||||||
@@ -353,15 +353,8 @@ uint8 *KTXHandler::parse(filesystem::FileData *filedata, std::vector<CompressedI
|
|||||||
fileoffset += mipsizepadded;
|
fileoffset += mipsizepadded;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 *data = nullptr;
|
StrongRef<CompressedImageData::Memory> memory;
|
||||||
try
|
memory.set(new CompressedImageData::Memory(totalsize), Acquire::NORETAIN);
|
||||||
{
|
|
||||||
data = new uint8[totalsize];
|
|
||||||
}
|
|
||||||
catch (std::bad_alloc &)
|
|
||||||
{
|
|
||||||
throw love::Exception("Out of memory.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset the file offset to the start of the file's image data.
|
// Reset the file offset to the start of the file's image data.
|
||||||
fileoffset = sizeof(KTXHeader) + header.bytesOfKeyValueData;
|
fileoffset = sizeof(KTXHeader) + header.bytesOfKeyValueData;
|
||||||
@@ -379,25 +372,23 @@ uint8 *KTXHandler::parse(filesystem::FileData *filedata, std::vector<CompressedI
|
|||||||
|
|
||||||
uint32 mipsizepadded = (mipsize + 3) & ~uint32(3);
|
uint32 mipsizepadded = (mipsize + 3) & ~uint32(3);
|
||||||
|
|
||||||
CompressedImageData::SubImage mip;
|
int width = (int) std::max(header.pixelWidth >> i, 1u);
|
||||||
mip.width = (int) std::max(header.pixelWidth >> i, 1u);
|
int height = (int) std::max(header.pixelHeight >> i, 1u);
|
||||||
mip.height = (int) std::max(header.pixelHeight >> i, 1u);
|
|
||||||
mip.size = mipsize;
|
|
||||||
|
|
||||||
memcpy(data + dataoffset, filebytes + fileoffset, mipsize);
|
memcpy(memory->data + dataoffset, filebytes + fileoffset, mipsize);
|
||||||
mip.data = data + dataoffset;
|
|
||||||
|
auto slice = new CompressedImageData::Slice(cformat, width, height, memory, dataoffset, mipsize);
|
||||||
|
images.push_back(slice);
|
||||||
|
slice->release();
|
||||||
|
|
||||||
fileoffset += mipsizepadded;
|
fileoffset += mipsizepadded;
|
||||||
dataoffset += mipsizepadded;
|
dataoffset += mipsizepadded;
|
||||||
|
|
||||||
images.push_back(mip);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dataSize = totalsize;
|
|
||||||
format = cformat;
|
format = cformat;
|
||||||
sRGB = isSRGB;
|
sRGB = isSRGB;
|
||||||
|
|
||||||
return data;
|
return memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // magpie
|
} // magpie
|
||||||
|
|||||||
@@ -41,8 +41,11 @@ public:
|
|||||||
virtual ~KTXHandler() {}
|
virtual ~KTXHandler() {}
|
||||||
|
|
||||||
// Implements CompressedFormatHandler.
|
// Implements CompressedFormatHandler.
|
||||||
virtual bool canParse(const filesystem::FileData *data);
|
bool canParse(const filesystem::FileData *data) override;
|
||||||
virtual uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedImageData::SubImage> &images, size_t &dataSize, PixelFormat &format, bool &sRGB);
|
|
||||||
|
StrongRef<CompressedImageData::Memory> parse(filesystem::FileData *filedata,
|
||||||
|
std::vector<StrongRef<CompressedImageData::Slice>> &images,
|
||||||
|
PixelFormat &format, bool &sRGB) override;
|
||||||
|
|
||||||
}; // KTXHandler
|
}; // KTXHandler
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ bool PKMHandler::canParse(const filesystem::FileData *data)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 *PKMHandler::parse(filesystem::FileData *filedata, std::vector<CompressedImageData::SubImage> &images, size_t &dataSize, PixelFormat &format, bool &sRGB)
|
StrongRef<CompressedImageData::Memory> PKMHandler::parse(filesystem::FileData *filedata, std::vector<StrongRef<CompressedImageData::Slice>> &images, PixelFormat &format, bool &sRGB)
|
||||||
{
|
{
|
||||||
if (!canParse(filedata))
|
if (!canParse(filedata))
|
||||||
throw love::Exception("Could not decode compressed data (not a PKM file?)");
|
throw love::Exception("Could not decode compressed data (not a PKM file?)");
|
||||||
@@ -133,37 +133,24 @@ uint8 *PKMHandler::parse(filesystem::FileData *filedata, std::vector<CompressedI
|
|||||||
|
|
||||||
// The rest of the file after the header is all texture data.
|
// The rest of the file after the header is all texture data.
|
||||||
size_t totalsize = filedata->getSize() - sizeof(PKMHeader);
|
size_t totalsize = filedata->getSize() - sizeof(PKMHeader);
|
||||||
uint8 *data = nullptr;
|
|
||||||
|
|
||||||
try
|
StrongRef<CompressedImageData::Memory> memory;
|
||||||
{
|
memory.set(new CompressedImageData::Memory(totalsize), Acquire::NORETAIN);
|
||||||
data = new uint8[totalsize];
|
|
||||||
}
|
|
||||||
catch (std::bad_alloc &)
|
|
||||||
{
|
|
||||||
throw love::Exception("Out of memory.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// PKM files only store a single mipmap level.
|
// PKM files only store a single mipmap level.
|
||||||
memcpy(data, (uint8 *) filedata->getData() + sizeof(PKMHeader), totalsize);
|
memcpy(memory->data, (uint8 *) filedata->getData() + sizeof(PKMHeader), totalsize);
|
||||||
|
|
||||||
CompressedImageData::SubImage mip;
|
|
||||||
|
|
||||||
// TODO: verify whether glCompressedTexImage works properly with the unpadded
|
// TODO: verify whether glCompressedTexImage works properly with the unpadded
|
||||||
// width and height values (extended == padded.)
|
// width and height values (extended == padded.)
|
||||||
mip.width = header.widthBig;
|
int width = header.widthBig;
|
||||||
mip.height = header.heightBig;
|
int height = header.heightBig;
|
||||||
|
|
||||||
mip.size = totalsize;
|
images.emplace_back(new CompressedImageData::Slice(cformat, width, height, memory, 0, totalsize), Acquire::NORETAIN);
|
||||||
mip.data = data;
|
|
||||||
|
|
||||||
images.push_back(mip);
|
|
||||||
|
|
||||||
dataSize = totalsize;
|
|
||||||
format = cformat;
|
format = cformat;
|
||||||
sRGB = false;
|
sRGB = false;
|
||||||
|
|
||||||
return data;
|
return memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // magpie
|
} // magpie
|
||||||
|
|||||||
@@ -41,8 +41,11 @@ public:
|
|||||||
virtual ~PKMHandler() {}
|
virtual ~PKMHandler() {}
|
||||||
|
|
||||||
// Implements CompressedFormatHandler.
|
// Implements CompressedFormatHandler.
|
||||||
virtual bool canParse(const filesystem::FileData *data);
|
bool canParse(const filesystem::FileData *data) override;
|
||||||
virtual uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedImageData::SubImage> &images, size_t &dataSize, PixelFormat &format, bool &sRGB);
|
|
||||||
|
StrongRef<CompressedImageData::Memory> parse(filesystem::FileData *filedata,
|
||||||
|
std::vector<StrongRef<CompressedImageData::Slice>> &images,
|
||||||
|
PixelFormat &format, bool &sRGB) override;
|
||||||
|
|
||||||
}; // PKMHandler
|
}; // PKMHandler
|
||||||
|
|
||||||
|
|||||||
@@ -474,7 +474,7 @@ bool PVRHandler::canParse(const filesystem::FileData *data)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 *PVRHandler::parse(filesystem::FileData *filedata, std::vector<CompressedImageData::SubImage> &images, size_t &dataSize, PixelFormat &format, bool &sRGB)
|
StrongRef<CompressedImageData::Memory> PVRHandler::parse(filesystem::FileData *filedata, std::vector<StrongRef<CompressedImageData::Slice>> &images, PixelFormat &format, bool &sRGB)
|
||||||
{
|
{
|
||||||
if (!canParse(filedata))
|
if (!canParse(filedata))
|
||||||
throw love::Exception("Could not decode compressed data (not a PVR file?)");
|
throw love::Exception("Could not decode compressed data (not a PVR file?)");
|
||||||
@@ -525,14 +525,8 @@ uint8 *PVRHandler::parse(filesystem::FileData *filedata, std::vector<CompressedI
|
|||||||
if (filedata->getSize() < fileoffset + totalsize)
|
if (filedata->getSize() < fileoffset + totalsize)
|
||||||
throw love::Exception("Could not parse PVR file: invalid size calculation.");
|
throw love::Exception("Could not parse PVR file: invalid size calculation.");
|
||||||
|
|
||||||
try
|
StrongRef<CompressedImageData::Memory> memory;
|
||||||
{
|
memory.set(new CompressedImageData::Memory(totalsize), Acquire::NORETAIN);
|
||||||
data = new uint8[totalsize];
|
|
||||||
}
|
|
||||||
catch (std::bad_alloc &)
|
|
||||||
{
|
|
||||||
throw love::Exception("Out of memory.");
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t curoffset = 0;
|
size_t curoffset = 0;
|
||||||
const uint8 *filebytes = (uint8 *) filedata->getData() + fileoffset;
|
const uint8 *filebytes = (uint8 *) filedata->getData() + fileoffset;
|
||||||
@@ -544,24 +538,22 @@ uint8 *PVRHandler::parse(filesystem::FileData *filedata, std::vector<CompressedI
|
|||||||
if (curoffset + mipsize > totalsize)
|
if (curoffset + mipsize > totalsize)
|
||||||
break; // Just in case.
|
break; // Just in case.
|
||||||
|
|
||||||
CompressedImageData::SubImage mip;
|
int width = std::max((int) header3.width >> i, 1);
|
||||||
mip.width = std::max((int) header3.width >> i, 1);
|
int height = std::max((int) header3.height >> i, 1);
|
||||||
mip.height = std::max((int) header3.height >> i, 1);
|
|
||||||
mip.size = mipsize;
|
|
||||||
|
|
||||||
memcpy(data + curoffset, filebytes + curoffset, mipsize);
|
memcpy(data + curoffset, filebytes + curoffset, mipsize);
|
||||||
mip.data = data + curoffset;
|
|
||||||
|
auto slice = new CompressedImageData::Slice(cformat, width, height, memory, curoffset, mipsize);
|
||||||
|
images.push_back(slice);
|
||||||
|
slice->release();
|
||||||
|
|
||||||
curoffset += mipsize;
|
curoffset += mipsize;
|
||||||
|
|
||||||
images.push_back(mip);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dataSize = totalsize;
|
|
||||||
format = cformat;
|
format = cformat;
|
||||||
sRGB = (header3.colorSpace == 1);
|
sRGB = (header3.colorSpace == 1);
|
||||||
|
|
||||||
return data;
|
return memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // magpie
|
} // magpie
|
||||||
|
|||||||
@@ -39,8 +39,11 @@ public:
|
|||||||
virtual ~PVRHandler() {}
|
virtual ~PVRHandler() {}
|
||||||
|
|
||||||
// Implements CompressedFormatHandler.
|
// Implements CompressedFormatHandler.
|
||||||
virtual bool canParse(const filesystem::FileData *data);
|
bool canParse(const filesystem::FileData *data) override;
|
||||||
virtual uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedImageData::SubImage> &images, size_t &dataSize, PixelFormat &format, bool &sRGB);
|
|
||||||
|
StrongRef<CompressedImageData::Memory> parse(filesystem::FileData *filedata,
|
||||||
|
std::vector<StrongRef<CompressedImageData::Slice>> &images,
|
||||||
|
PixelFormat &format, bool &sRGB) override;
|
||||||
|
|
||||||
}; // PVRHandler
|
}; // PVRHandler
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ bool DDSHandler::canParse(const filesystem::FileData *data)
|
|||||||
return dds::isCompressedDDS(data->getData(), data->getSize());
|
return dds::isCompressedDDS(data->getData(), data->getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 *DDSHandler::parse(filesystem::FileData *filedata, std::vector<CompressedImageData::SubImage> &images, size_t &dataSize, PixelFormat &format, bool &sRGB)
|
StrongRef<CompressedImageData::Memory> DDSHandler::parse(filesystem::FileData *filedata, std::vector<StrongRef<CompressedImageData::Slice>> &images, PixelFormat &format, bool &sRGB)
|
||||||
{
|
{
|
||||||
if (!dds::isDDS(filedata->getData(), filedata->getSize()))
|
if (!dds::isDDS(filedata->getData(), filedata->getSize()))
|
||||||
throw love::Exception("Could not decode compressed data (not a DDS file?)");
|
throw love::Exception("Could not decode compressed data (not a DDS file?)");
|
||||||
@@ -40,12 +40,11 @@ uint8 *DDSHandler::parse(filesystem::FileData *filedata, std::vector<CompressedI
|
|||||||
PixelFormat texformat = PIXELFORMAT_UNKNOWN;
|
PixelFormat texformat = PIXELFORMAT_UNKNOWN;
|
||||||
bool isSRGB = false;
|
bool isSRGB = false;
|
||||||
|
|
||||||
uint8 *data = nullptr;
|
StrongRef<CompressedImageData::Memory> memory;
|
||||||
dataSize = 0;
|
size_t dataSize = 0;
|
||||||
|
|
||||||
images.clear();
|
images.clear();
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Attempt to parse the dds file.
|
// Attempt to parse the dds file.
|
||||||
dds::Parser parser(filedata->getData(), filedata->getSize());
|
dds::Parser parser(filedata->getData(), filedata->getSize());
|
||||||
|
|
||||||
@@ -64,7 +63,7 @@ uint8 *DDSHandler::parse(filesystem::FileData *filedata, std::vector<CompressedI
|
|||||||
dataSize += img->dataSize;
|
dataSize += img->dataSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = new uint8[dataSize];
|
memory.set(new CompressedImageData::Memory(dataSize), Acquire::NORETAIN);
|
||||||
|
|
||||||
size_t dataOffset = 0;
|
size_t dataOffset = 0;
|
||||||
|
|
||||||
@@ -74,31 +73,18 @@ uint8 *DDSHandler::parse(filesystem::FileData *filedata, std::vector<CompressedI
|
|||||||
// Fetch the data for this mipmap level.
|
// Fetch the data for this mipmap level.
|
||||||
const dds::Image *img = parser.getImageData(i);
|
const dds::Image *img = parser.getImageData(i);
|
||||||
|
|
||||||
CompressedImageData::SubImage mip;
|
|
||||||
|
|
||||||
mip.width = img->width;
|
|
||||||
mip.height = img->height;
|
|
||||||
mip.size = img->dataSize;
|
|
||||||
|
|
||||||
// Copy the mipmap image from the FileData to our block of memory.
|
// Copy the mipmap image from the FileData to our block of memory.
|
||||||
memcpy(data + dataOffset, img->data, mip.size);
|
memcpy(memory->data + dataOffset, img->data, img->dataSize);
|
||||||
mip.data = data + dataOffset;
|
|
||||||
|
|
||||||
dataOffset += mip.size;
|
auto slice = new CompressedImageData::Slice(texformat, img->width, img->height, memory, dataOffset, img->dataSize);
|
||||||
|
images.emplace_back(slice, Acquire::NORETAIN);
|
||||||
|
|
||||||
images.push_back(mip);
|
dataOffset += img->dataSize;
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (std::exception &e)
|
|
||||||
{
|
|
||||||
delete[] data;
|
|
||||||
images.clear();
|
|
||||||
throw love::Exception("%s", e.what());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
format = texformat;
|
format = texformat;
|
||||||
sRGB = isSRGB;
|
sRGB = isSRGB;
|
||||||
return data;
|
return memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
PixelFormat DDSHandler::convertFormat(dds::Format ddsformat, bool &sRGB)
|
PixelFormat DDSHandler::convertFormat(dds::Format ddsformat, bool &sRGB)
|
||||||
|
|||||||
@@ -47,8 +47,11 @@ public:
|
|||||||
virtual ~DDSHandler() {}
|
virtual ~DDSHandler() {}
|
||||||
|
|
||||||
// Implements CompressedFormatHandler.
|
// Implements CompressedFormatHandler.
|
||||||
virtual bool canParse(const filesystem::FileData *data);
|
bool canParse(const filesystem::FileData *data) override;
|
||||||
virtual uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedImageData::SubImage> &images, size_t &dataSize, PixelFormat &format, bool &sRGB);
|
|
||||||
|
StrongRef<CompressedImageData::Memory> parse(filesystem::FileData *filedata,
|
||||||
|
std::vector<StrongRef<CompressedImageData::Slice>> &images,
|
||||||
|
PixelFormat &format, bool &sRGB) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|||||||
@@ -122,12 +122,23 @@ int w_isCompressed(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int w_newCubeFaces(lua_State *L)
|
||||||
|
{
|
||||||
|
ImageData *id = luax_checkimagedata(L, 1);
|
||||||
|
std::vector<StrongRef<ImageData>> faces;
|
||||||
|
luax_catchexcept(L, [&](){ faces = instance()->newCubeFaces(id); });
|
||||||
|
for (auto face : faces)
|
||||||
|
luax_pushtype(L, face);
|
||||||
|
return (int) faces.size();
|
||||||
|
}
|
||||||
|
|
||||||
// List of functions to wrap.
|
// List of functions to wrap.
|
||||||
static const luaL_Reg functions[] =
|
static const luaL_Reg functions[] =
|
||||||
{
|
{
|
||||||
{ "newImageData", w_newImageData },
|
{ "newImageData", w_newImageData },
|
||||||
{ "newCompressedData", w_newCompressedData },
|
{ "newCompressedData", w_newCompressedData },
|
||||||
{ "isCompressed", w_isCompressed },
|
{ "isCompressed", w_isCompressed },
|
||||||
|
{ "newCubeFaces", w_newCubeFaces },
|
||||||
{ 0, 0 }
|
{ 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user