mirror of
https://github.com/love2d/love.git
synced 2026-08-18 03:34:23 +02:00
Merge branch '12.0-development' into metal
This commit is contained in:
@@ -182,6 +182,21 @@ private:
|
||||
|
||||
}; // StringMap
|
||||
|
||||
#define STRINGMAP_DECLARE(type) \
|
||||
bool getConstant(const char *in, type &out); \
|
||||
bool getConstant(type in, const char *&out); \
|
||||
std::vector<std::string> getConstants(type); \
|
||||
|
||||
#define STRINGMAP_BEGIN(type, count, name) \
|
||||
static StringMap<type, count>::Entry name##Entries[] =
|
||||
|
||||
#define STRINGMAP_END(type, count, name) \
|
||||
; \
|
||||
static StringMap<type, count> name##s(name##Entries, sizeof(name##Entries)); \
|
||||
bool getConstant(const char *in, type &out) { return name##s.find(in, out); } \
|
||||
bool getConstant(type in, const char *&out) { return name##s.find(in, out); } \
|
||||
std::vector<std::string> getConstants(type) { return name##s.getNames(); }
|
||||
|
||||
} // love
|
||||
|
||||
#endif // LOVE_STRING_MAP_H
|
||||
|
||||
+2859
-2681
File diff suppressed because it is too large
Load Diff
+319
-136
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
LodePNG version 20170917
|
||||
LodePNG version 20200306
|
||||
|
||||
Copyright (c) 2005-2017 Lode Vandevenne
|
||||
Copyright (c) 2005-2020 Lode Vandevenne
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
@@ -44,36 +44,44 @@ the custom_zlib field of the compress and decompress settings*/
|
||||
#ifndef LODEPNG_NO_COMPILE_ZLIB
|
||||
#define LODEPNG_COMPILE_ZLIB
|
||||
#endif
|
||||
|
||||
/*png encoder and png decoder*/
|
||||
#ifndef LODEPNG_NO_COMPILE_PNG
|
||||
#define LODEPNG_COMPILE_PNG
|
||||
#endif
|
||||
|
||||
/*deflate&zlib decoder and png decoder*/
|
||||
#ifndef LODEPNG_NO_COMPILE_DECODER
|
||||
#define LODEPNG_COMPILE_DECODER
|
||||
#endif
|
||||
|
||||
/*deflate&zlib encoder and png encoder*/
|
||||
#ifndef LODEPNG_NO_COMPILE_ENCODER
|
||||
#define LODEPNG_COMPILE_ENCODER
|
||||
#endif
|
||||
|
||||
/*the optional built in harddisk file loading and saving functions*/
|
||||
#ifndef LODEPNG_NO_COMPILE_DISK
|
||||
#define LODEPNG_COMPILE_DISK
|
||||
#endif
|
||||
|
||||
/*support for chunks other than IHDR, IDAT, PLTE, tRNS, IEND: ancillary and unknown chunks*/
|
||||
#ifndef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
|
||||
#define LODEPNG_COMPILE_ANCILLARY_CHUNKS
|
||||
#endif
|
||||
|
||||
/*ability to convert error numerical codes to English text string*/
|
||||
#ifndef LODEPNG_NO_COMPILE_ERROR_TEXT
|
||||
#define LODEPNG_COMPILE_ERROR_TEXT
|
||||
#endif
|
||||
|
||||
/*Compile the default allocators (C's free, malloc and realloc). If you disable this,
|
||||
you can define the functions lodepng_free, lodepng_malloc and lodepng_realloc in your
|
||||
source files with custom allocators.*/
|
||||
#ifndef LODEPNG_NO_COMPILE_ALLOCATORS
|
||||
#define LODEPNG_COMPILE_ALLOCATORS
|
||||
#endif
|
||||
|
||||
/*compile the C++ version (you can disable the C++ wrapper here even when compiling for C++)*/
|
||||
#ifdef __cplusplus
|
||||
#ifndef LODEPNG_NO_COMPILE_CPP
|
||||
@@ -87,14 +95,19 @@ source files with custom allocators.*/
|
||||
#endif /*LODEPNG_COMPILE_CPP*/
|
||||
|
||||
#ifdef LODEPNG_COMPILE_PNG
|
||||
/*The PNG color types (also used for raw).*/
|
||||
typedef enum LodePNGColorType
|
||||
{
|
||||
LCT_GREY = 0, /*greyscale: 1,2,4,8,16 bit*/
|
||||
/*The PNG color types (also used for raw image).*/
|
||||
typedef enum LodePNGColorType {
|
||||
LCT_GREY = 0, /*grayscale: 1,2,4,8,16 bit*/
|
||||
LCT_RGB = 2, /*RGB: 8,16 bit*/
|
||||
LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/
|
||||
LCT_GREY_ALPHA = 4, /*greyscale with alpha: 8,16 bit*/
|
||||
LCT_RGBA = 6 /*RGB with alpha: 8,16 bit*/
|
||||
LCT_GREY_ALPHA = 4, /*grayscale with alpha: 8,16 bit*/
|
||||
LCT_RGBA = 6, /*RGB with alpha: 8,16 bit*/
|
||||
/*LCT_MAX_OCTET_VALUE lets the compiler allow this enum to represent any invalid
|
||||
byte value from 0 to 255 that could be present in an invalid PNG file header. Do
|
||||
not use, compare with or set the name LCT_MAX_OCTET_VALUE, instead either use
|
||||
the valid color type names above, or numeric values like 1 or 7 when checking for
|
||||
particular disallowed color type byte values, or cast to integer to print it.*/
|
||||
LCT_MAX_OCTET_VALUE = 255
|
||||
} LodePNGColorType;
|
||||
|
||||
#ifdef LODEPNG_COMPILE_DECODER
|
||||
@@ -196,8 +209,7 @@ unsigned lodepng_encode24_file(const char* filename,
|
||||
|
||||
|
||||
#ifdef LODEPNG_COMPILE_CPP
|
||||
namespace lodepng
|
||||
{
|
||||
namespace lodepng {
|
||||
#ifdef LODEPNG_COMPILE_DECODER
|
||||
/*Same as lodepng_decode_memory, but decodes to an std::vector. The colortype
|
||||
is the format to output the pixels to. Default is RGBA 8-bit per channel.*/
|
||||
@@ -253,17 +265,17 @@ const char* lodepng_error_text(unsigned code);
|
||||
#ifdef LODEPNG_COMPILE_DECODER
|
||||
/*Settings for zlib decompression*/
|
||||
typedef struct LodePNGDecompressSettings LodePNGDecompressSettings;
|
||||
struct LodePNGDecompressSettings
|
||||
{
|
||||
struct LodePNGDecompressSettings {
|
||||
/* Check LodePNGDecoderSettings for more ignorable errors such as ignore_crc */
|
||||
unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/
|
||||
unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/
|
||||
|
||||
/*use custom zlib decoder instead of built in one (default: null)*/
|
||||
unsigned (*custom_zlib)(unsigned char**, size_t*,
|
||||
const unsigned char*, size_t,
|
||||
const LodePNGDecompressSettings*);
|
||||
/*use custom deflate decoder instead of built in one (default: null)
|
||||
if custom_zlib is used, custom_deflate is ignored since only the built in
|
||||
zlib function will call custom_deflate*/
|
||||
if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate)*/
|
||||
unsigned (*custom_inflate)(unsigned char**, size_t*,
|
||||
const unsigned char*, size_t,
|
||||
const LodePNGDecompressSettings*);
|
||||
@@ -281,13 +293,12 @@ Settings for zlib compression. Tweaking these settings tweaks the balance
|
||||
between speed and compression ratio.
|
||||
*/
|
||||
typedef struct LodePNGCompressSettings LodePNGCompressSettings;
|
||||
struct LodePNGCompressSettings /*deflate = compress*/
|
||||
{
|
||||
struct LodePNGCompressSettings /*deflate = compress*/ {
|
||||
/*LZ77 related settings*/
|
||||
unsigned btype; /*the block type for LZ (0, 1, 2 or 3, see zlib standard). Should be 2 for proper compression.*/
|
||||
unsigned use_lz77; /*whether or not to use LZ77. Should be 1 for proper compression.*/
|
||||
unsigned windowsize; /*must be a power of two <= 32768. higher compresses more but is slower. Default value: 2048.*/
|
||||
unsigned minmatch; /*mininum lz77 length. 3 is normally best, 6 can be better for some PNGs. Default: 0*/
|
||||
unsigned minmatch; /*minimum lz77 length. 3 is normally best, 6 can be better for some PNGs. Default: 0*/
|
||||
unsigned nicematch; /*stop searching if >= this length found. Set to 258 for best compression. Default: 128*/
|
||||
unsigned lazymatching; /*use lazy matching: better compression but a bit slower. Default: true*/
|
||||
|
||||
@@ -315,8 +326,7 @@ Color mode of an image. Contains all information required to decode the pixel
|
||||
bits to RGBA colors. This information is the same as used in the PNG file
|
||||
format, and is used both for PNG and raw image data in LodePNG.
|
||||
*/
|
||||
typedef struct LodePNGColorMode
|
||||
{
|
||||
typedef struct LodePNGColorMode {
|
||||
/*header (IHDR)*/
|
||||
LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/
|
||||
unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/
|
||||
@@ -325,23 +335,25 @@ typedef struct LodePNGColorMode
|
||||
palette (PLTE and tRNS)
|
||||
|
||||
Dynamically allocated with the colors of the palette, including alpha.
|
||||
When encoding a PNG, to store your colors in the palette of the LodePNGColorMode, first use
|
||||
lodepng_palette_clear, then for each color use lodepng_palette_add.
|
||||
If you encode an image without alpha with palette, don't forget to put value 255 in each A byte of the palette.
|
||||
This field may not be allocated directly, use lodepng_color_mode_init first,
|
||||
then lodepng_palette_add per color to correctly initialize it (to ensure size
|
||||
of exactly 1024 bytes).
|
||||
|
||||
The alpha channels must be set as well, set them to 255 for opaque images.
|
||||
|
||||
When decoding, by default you can ignore this palette, since LodePNG already
|
||||
fills the palette colors in the pixels of the raw RGBA output.
|
||||
|
||||
The palette is only supported for color type 3.
|
||||
*/
|
||||
unsigned char* palette; /*palette in RGBARGBA... order. When allocated, must be either 0, or have size 1024*/
|
||||
size_t palettesize; /*palette size in number of colors (amount of bytes is 4 * palettesize)*/
|
||||
unsigned char* palette; /*palette in RGBARGBA... order. Must be either 0, or when allocated must have 1024 bytes*/
|
||||
size_t palettesize; /*palette size in number of colors (amount of used bytes is 4 * palettesize)*/
|
||||
|
||||
/*
|
||||
transparent color key (tRNS)
|
||||
|
||||
This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit.
|
||||
For greyscale PNGs, r, g and b will all 3 be set to the same.
|
||||
For grayscale PNGs, r, g and b will all 3 be set to the same.
|
||||
|
||||
When decoding, by default you can ignore this information, since LodePNG sets
|
||||
pixels with this key to transparent already in the raw RGBA output.
|
||||
@@ -349,7 +361,7 @@ typedef struct LodePNGColorMode
|
||||
The color key is only supported for color types 0 and 2.
|
||||
*/
|
||||
unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/
|
||||
unsigned key_r; /*red/greyscale component of color key*/
|
||||
unsigned key_r; /*red/grayscale component of color key*/
|
||||
unsigned key_g; /*green component of color key*/
|
||||
unsigned key_b; /*blue component of color key*/
|
||||
} LodePNGColorMode;
|
||||
@@ -359,6 +371,8 @@ void lodepng_color_mode_init(LodePNGColorMode* info);
|
||||
void lodepng_color_mode_cleanup(LodePNGColorMode* info);
|
||||
/*return value is error code (0 means no error)*/
|
||||
unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source);
|
||||
/* Makes a temporary LodePNGColorMode that does not need cleanup (no palette) */
|
||||
LodePNGColorMode lodepng_color_mode_make(LodePNGColorType colortype, unsigned bitdepth);
|
||||
|
||||
void lodepng_palette_clear(LodePNGColorMode* info);
|
||||
/*add 1 color to the palette*/
|
||||
@@ -370,7 +384,7 @@ unsigned lodepng_get_bpp(const LodePNGColorMode* info);
|
||||
/*get the amount of color channels used, based on colortype in the struct.
|
||||
If a palette is used, it counts as 1 channel.*/
|
||||
unsigned lodepng_get_channels(const LodePNGColorMode* info);
|
||||
/*is it a greyscale type? (only colortype 0 or 4)*/
|
||||
/*is it a grayscale type? (only colortype 0 or 4)*/
|
||||
unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info);
|
||||
/*has it got an alpha channel? (only colortype 2 or 6)*/
|
||||
unsigned lodepng_is_alpha_type(const LodePNGColorMode* info);
|
||||
@@ -392,8 +406,7 @@ size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* colo
|
||||
|
||||
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
|
||||
/*The information of a Time chunk in PNG.*/
|
||||
typedef struct LodePNGTime
|
||||
{
|
||||
typedef struct LodePNGTime {
|
||||
unsigned year; /*2 bytes used (0-65535)*/
|
||||
unsigned month; /*1-12*/
|
||||
unsigned day; /*1-31*/
|
||||
@@ -404,28 +417,39 @@ typedef struct LodePNGTime
|
||||
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
|
||||
|
||||
/*Information about the PNG image, except pixels, width and height.*/
|
||||
typedef struct LodePNGInfo
|
||||
{
|
||||
typedef struct LodePNGInfo {
|
||||
/*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/
|
||||
unsigned compression_method;/*compression method of the original file. Always 0.*/
|
||||
unsigned filter_method; /*filter method of the original file*/
|
||||
unsigned interlace_method; /*interlace method of the original file*/
|
||||
unsigned interlace_method; /*interlace method of the original file: 0=none, 1=Adam7*/
|
||||
LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/
|
||||
|
||||
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
|
||||
/*
|
||||
suggested background color chunk (bKGD)
|
||||
This color uses the same color mode as the PNG (except alpha channel), which can be 1-bit to 16-bit.
|
||||
Suggested background color chunk (bKGD)
|
||||
|
||||
For greyscale PNGs, r, g and b will all 3 be set to the same. When encoding
|
||||
the encoder writes the red one. For palette PNGs: When decoding, the RGB value
|
||||
will be stored, not a palette index. But when encoding, specify the index of
|
||||
the palette in background_r, the other two are then ignored.
|
||||
This uses the same color mode and bit depth as the PNG (except no alpha channel),
|
||||
with values truncated to the bit depth in the unsigned integer.
|
||||
|
||||
The decoder does not use this background color to edit the color of pixels.
|
||||
For grayscale and palette PNGs, the value is stored in background_r. The values
|
||||
in background_g and background_b are then unused.
|
||||
|
||||
So when decoding, you may get these in a different color mode than the one you requested
|
||||
for the raw pixels.
|
||||
|
||||
When encoding with auto_convert, you must use the color model defined in info_png.color for
|
||||
these values. The encoder normally ignores info_png.color when auto_convert is on, but will
|
||||
use it to interpret these values (and convert copies of them to its chosen color model).
|
||||
|
||||
When encoding, avoid setting this to an expensive color, such as a non-gray value
|
||||
when the image is gray, or the compression will be worse since it will be forced to
|
||||
write the PNG with a more expensive color mode (when auto_convert is on).
|
||||
|
||||
The decoder does not use this background color to edit the color of pixels. This is a
|
||||
completely optional metadata feature.
|
||||
*/
|
||||
unsigned background_defined; /*is a suggested background color given?*/
|
||||
unsigned background_r; /*red component of suggested background color*/
|
||||
unsigned background_r; /*red/gray/palette component of suggested background color*/
|
||||
unsigned background_g; /*green component of suggested background color*/
|
||||
unsigned background_b; /*blue component of suggested background color*/
|
||||
|
||||
@@ -436,6 +460,10 @@ typedef struct LodePNGInfo
|
||||
text_strings, while text_keys are keywords that give a short description what
|
||||
the actual text represents, e.g. Title, Author, Description, or anything else.
|
||||
|
||||
All the string fields below including keys, names and language tags are null terminated.
|
||||
The PNG specification uses null characters for the keys, names and tags, and forbids null
|
||||
characters to appear in the main text which is why we can use null termination everywhere here.
|
||||
|
||||
A keyword is minimum 1 character and maximum 79 characters long. It's
|
||||
discouraged to use a single line length longer than 79 characters for texts.
|
||||
|
||||
@@ -468,11 +496,86 @@ typedef struct LodePNGInfo
|
||||
unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/
|
||||
|
||||
/*
|
||||
unknown chunks
|
||||
There are 3 buffers, one for each position in the PNG where unknown chunks can appear
|
||||
each buffer contains all unknown chunks for that position consecutively
|
||||
The 3 buffers are the unknown chunks between certain critical chunks:
|
||||
0: IHDR-PLTE, 1: PLTE-IDAT, 2: IDAT-IEND
|
||||
Color profile related chunks: gAMA, cHRM, sRGB, iCPP
|
||||
|
||||
LodePNG does not apply any color conversions on pixels in the encoder or decoder and does not interpret these color
|
||||
profile values. It merely passes on the information. If you wish to use color profiles and convert colors, please
|
||||
use these values with a color management library.
|
||||
|
||||
See the PNG, ICC and sRGB specifications for more information about the meaning of these values.
|
||||
*/
|
||||
|
||||
/* gAMA chunk: optional, overridden by sRGB or iCCP if those are present. */
|
||||
unsigned gama_defined; /* Whether a gAMA chunk is present (0 = not present, 1 = present). */
|
||||
unsigned gama_gamma; /* Gamma exponent times 100000 */
|
||||
|
||||
/* cHRM chunk: optional, overridden by sRGB or iCCP if those are present. */
|
||||
unsigned chrm_defined; /* Whether a cHRM chunk is present (0 = not present, 1 = present). */
|
||||
unsigned chrm_white_x; /* White Point x times 100000 */
|
||||
unsigned chrm_white_y; /* White Point y times 100000 */
|
||||
unsigned chrm_red_x; /* Red x times 100000 */
|
||||
unsigned chrm_red_y; /* Red y times 100000 */
|
||||
unsigned chrm_green_x; /* Green x times 100000 */
|
||||
unsigned chrm_green_y; /* Green y times 100000 */
|
||||
unsigned chrm_blue_x; /* Blue x times 100000 */
|
||||
unsigned chrm_blue_y; /* Blue y times 100000 */
|
||||
|
||||
/*
|
||||
sRGB chunk: optional. May not appear at the same time as iCCP.
|
||||
If gAMA is also present gAMA must contain value 45455.
|
||||
If cHRM is also present cHRM must contain respectively 31270,32900,64000,33000,30000,60000,15000,6000.
|
||||
*/
|
||||
unsigned srgb_defined; /* Whether an sRGB chunk is present (0 = not present, 1 = present). */
|
||||
unsigned srgb_intent; /* Rendering intent: 0=perceptual, 1=rel. colorimetric, 2=saturation, 3=abs. colorimetric */
|
||||
|
||||
/*
|
||||
iCCP chunk: optional. May not appear at the same time as sRGB.
|
||||
|
||||
LodePNG does not parse or use the ICC profile (except its color space header field for an edge case), a
|
||||
separate library to handle the ICC data (not included in LodePNG) format is needed to use it for color
|
||||
management and conversions.
|
||||
|
||||
For encoding, if iCCP is present, gAMA and cHRM are recommended to be added as well with values that match the ICC
|
||||
profile as closely as possible, if you wish to do this you should provide the correct values for gAMA and cHRM and
|
||||
enable their '_defined' flags since LodePNG will not automatically compute them from the ICC profile.
|
||||
|
||||
For encoding, the ICC profile is required by the PNG specification to be an "RGB" profile for non-gray
|
||||
PNG color types and a "GRAY" profile for gray PNG color types. If you disable auto_convert, you must ensure
|
||||
the ICC profile type matches your requested color type, else the encoder gives an error. If auto_convert is
|
||||
enabled (the default), and the ICC profile is not a good match for the pixel data, this will result in an encoder
|
||||
error if the pixel data has non-gray pixels for a GRAY profile, or a silent less-optimal compression of the pixel
|
||||
data if the pixels could be encoded as grayscale but the ICC profile is RGB.
|
||||
|
||||
To avoid this do not set an ICC profile in the image unless there is a good reason for it, and when doing so
|
||||
make sure you compute it carefully to avoid the above problems.
|
||||
*/
|
||||
unsigned iccp_defined; /* Whether an iCCP chunk is present (0 = not present, 1 = present). */
|
||||
char* iccp_name; /* Null terminated string with profile name, 1-79 bytes */
|
||||
/*
|
||||
The ICC profile in iccp_profile_size bytes.
|
||||
Don't allocate this buffer yourself. Use the init/cleanup functions
|
||||
correctly and use lodepng_set_icc and lodepng_clear_icc.
|
||||
*/
|
||||
unsigned char* iccp_profile;
|
||||
unsigned iccp_profile_size; /* The size of iccp_profile in bytes */
|
||||
|
||||
/* End of color profile related chunks */
|
||||
|
||||
|
||||
/*
|
||||
unknown chunks: chunks not known by LodePNG, passed on byte for byte.
|
||||
|
||||
There are 3 buffers, one for each position in the PNG where unknown chunks can appear.
|
||||
Each buffer contains all unknown chunks for that position consecutively.
|
||||
The 3 positions are:
|
||||
0: between IHDR and PLTE, 1: between PLTE and IDAT, 2: between IDAT and IEND.
|
||||
|
||||
For encoding, do not store critical chunks or known chunks that are enabled with a "_defined" flag
|
||||
above in here, since the encoder will blindly follow this and could then encode an invalid PNG file
|
||||
(such as one with two IHDR chunks or the disallowed combination of sRGB with iCCP). But do use
|
||||
this if you wish to store an ancillary chunk that is not supported by LodePNG (such as sPLT or hIST),
|
||||
or any non-standard PNG chunk.
|
||||
|
||||
Do not allocate or traverse this data yourself. Use the chunk traversing functions declared
|
||||
later, such as lodepng_chunk_next and lodepng_chunk_append, to read/write this struct.
|
||||
*/
|
||||
@@ -488,12 +591,16 @@ void lodepng_info_cleanup(LodePNGInfo* info);
|
||||
unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source);
|
||||
|
||||
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
|
||||
void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/
|
||||
unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str); /*push back both texts at once*/
|
||||
void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/
|
||||
|
||||
void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/
|
||||
unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag,
|
||||
const char* transkey, const char* str); /*push back the 4 texts of 1 chunk at once*/
|
||||
void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/
|
||||
|
||||
/*replaces if exists*/
|
||||
unsigned lodepng_set_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size);
|
||||
void lodepng_clear_icc(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/
|
||||
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
|
||||
|
||||
/*
|
||||
@@ -516,11 +623,17 @@ unsigned lodepng_convert(unsigned char* out, const unsigned char* in,
|
||||
Settings for the decoder. This contains settings for the PNG and the Zlib
|
||||
decoder, but not the Info settings from the Info structs.
|
||||
*/
|
||||
typedef struct LodePNGDecoderSettings
|
||||
{
|
||||
typedef struct LodePNGDecoderSettings {
|
||||
LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/
|
||||
|
||||
/* Check LodePNGDecompressSettings for more ignorable errors such as ignore_adler32 */
|
||||
unsigned ignore_crc; /*ignore CRC checksums*/
|
||||
unsigned ignore_critical; /*ignore unknown critical chunks*/
|
||||
unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/
|
||||
/* TODO: make a system involving warnings with levels and a strict mode instead. Other potentially recoverable
|
||||
errors: srgb rendering intent value, size of content of ancillary chunks, more than 79 characters for some
|
||||
strings, placement/combination rules for ancillary chunks, crc of unknown chunks, allowed characters
|
||||
in string keys, etc... */
|
||||
|
||||
unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/
|
||||
|
||||
@@ -536,10 +649,14 @@ void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings);
|
||||
|
||||
#ifdef LODEPNG_COMPILE_ENCODER
|
||||
/*automatically use color type with less bits per pixel if losslessly possible. Default: AUTO*/
|
||||
typedef enum LodePNGFilterStrategy
|
||||
{
|
||||
typedef enum LodePNGFilterStrategy {
|
||||
/*every filter at zero*/
|
||||
LFS_ZERO,
|
||||
LFS_ZERO = 0,
|
||||
/*every filter at 1, 2, 3 or 4 (paeth), unlike LFS_ZERO not a good choice, but for testing*/
|
||||
LFS_ONE = 1,
|
||||
LFS_TWO = 2,
|
||||
LFS_THREE = 3,
|
||||
LFS_FOUR = 4,
|
||||
/*Use filter that gives minimum sum, as described in the official PNG filter heuristic.*/
|
||||
LFS_MINSUM,
|
||||
/*Use the filter type that gives smallest Shannon entropy for this scanline. Depending
|
||||
@@ -554,36 +671,36 @@ typedef enum LodePNGFilterStrategy
|
||||
LFS_PREDEFINED
|
||||
} LodePNGFilterStrategy;
|
||||
|
||||
/*Gives characteristics about the colors of the image, which helps decide which color model to use for encoding.
|
||||
/*Gives characteristics about the integer RGBA colors of the image (count, alpha channel usage, bit depth, ...),
|
||||
which helps decide which color model to use for encoding.
|
||||
Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms.*/
|
||||
typedef struct LodePNGColorProfile
|
||||
{
|
||||
unsigned colored; /*not greyscale*/
|
||||
typedef struct LodePNGColorStats {
|
||||
unsigned colored; /*not grayscale*/
|
||||
unsigned key; /*image is not opaque and color key is possible instead of full alpha*/
|
||||
unsigned short key_r; /*key values, always as 16-bit, in 8-bit case the byte is duplicated, e.g. 65535 means 255*/
|
||||
unsigned short key_g;
|
||||
unsigned short key_b;
|
||||
unsigned alpha; /*image is not opaque and alpha channel or alpha palette required*/
|
||||
unsigned numcolors; /*amount of colors, up to 257. Not valid if bits == 16.*/
|
||||
unsigned char palette[1024]; /*Remembers up to the first 256 RGBA colors, in no particular order*/
|
||||
unsigned bits; /*bits per channel (not for palette). 1,2 or 4 for greyscale only. 16 if 16-bit per channel required.*/
|
||||
} LodePNGColorProfile;
|
||||
unsigned numcolors; /*amount of colors, up to 257. Not valid if bits == 16 or allow_palette is disabled.*/
|
||||
unsigned char palette[1024]; /*Remembers up to the first 256 RGBA colors, in no particular order, only valid when numcolors is valid*/
|
||||
unsigned bits; /*bits per channel (not for palette). 1,2 or 4 for grayscale only. 16 if 16-bit per channel required.*/
|
||||
size_t numpixels;
|
||||
|
||||
void lodepng_color_profile_init(LodePNGColorProfile* profile);
|
||||
/*user settings for computing/using the stats*/
|
||||
unsigned allow_palette; /*default 1. if 0, disallow choosing palette colortype in auto_choose_color, and don't count numcolors*/
|
||||
unsigned allow_greyscale; /*default 1. if 0, choose RGB or RGBA even if the image only has gray colors*/
|
||||
} LodePNGColorStats;
|
||||
|
||||
/*Get a LodePNGColorProfile of the image.*/
|
||||
unsigned lodepng_get_color_profile(LodePNGColorProfile* profile,
|
||||
const unsigned char* image, unsigned w, unsigned h,
|
||||
const LodePNGColorMode* mode_in);
|
||||
/*The function LodePNG uses internally to decide the PNG color with auto_convert.
|
||||
Chooses an optimal color model, e.g. grey if only grey pixels, palette if < 256 colors, ...*/
|
||||
unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out,
|
||||
const unsigned char* image, unsigned w, unsigned h,
|
||||
const LodePNGColorMode* mode_in);
|
||||
void lodepng_color_stats_init(LodePNGColorStats* stats);
|
||||
|
||||
/*Get a LodePNGColorStats of the image. The stats must already have been inited.
|
||||
Returns error code (e.g. alloc fail) or 0 if ok.*/
|
||||
unsigned lodepng_compute_color_stats(LodePNGColorStats* stats,
|
||||
const unsigned char* image, unsigned w, unsigned h,
|
||||
const LodePNGColorMode* mode_in);
|
||||
|
||||
/*Settings for the encoder.*/
|
||||
typedef struct LodePNGEncoderSettings
|
||||
{
|
||||
typedef struct LodePNGEncoderSettings {
|
||||
LodePNGCompressSettings zlibsettings; /*settings for the zlib encoder, such as window size, ...*/
|
||||
|
||||
unsigned auto_convert; /*automatically choose output PNG color type. Default: true*/
|
||||
@@ -619,8 +736,7 @@ void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings);
|
||||
|
||||
#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER)
|
||||
/*The settings, state and information for extended encoding and decoding.*/
|
||||
typedef struct LodePNGState
|
||||
{
|
||||
typedef struct LodePNGState {
|
||||
#ifdef LODEPNG_COMPILE_DECODER
|
||||
LodePNGDecoderSettings decoder; /*the decoding settings*/
|
||||
#endif /*LODEPNG_COMPILE_DECODER*/
|
||||
@@ -630,10 +746,6 @@ typedef struct LodePNGState
|
||||
LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/
|
||||
LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/
|
||||
unsigned error;
|
||||
#ifdef LODEPNG_COMPILE_CPP
|
||||
/* For the lodepng::State subclass. */
|
||||
virtual ~LodePNGState(){}
|
||||
#endif
|
||||
} LodePNGState;
|
||||
|
||||
/*init, cleanup and copy functions to use with this struct*/
|
||||
@@ -653,7 +765,7 @@ unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h,
|
||||
|
||||
/*
|
||||
Read the PNG header, but not the actual data. This returns only the information
|
||||
that is in the header chunk of the PNG, such as width, height and color type. The
|
||||
that is in the IHDR chunk of the PNG, such as width, height and color type. The
|
||||
information is placed in the info_png field of the LodePNGState.
|
||||
*/
|
||||
unsigned lodepng_inspect(unsigned* w, unsigned* h,
|
||||
@@ -661,6 +773,20 @@ unsigned lodepng_inspect(unsigned* w, unsigned* h,
|
||||
const unsigned char* in, size_t insize);
|
||||
#endif /*LODEPNG_COMPILE_DECODER*/
|
||||
|
||||
/*
|
||||
Reads one metadata chunk (other than IHDR) of the PNG file and outputs what it
|
||||
read in the state. Returns error code on failure.
|
||||
Use lodepng_inspect first with a new state, then e.g. lodepng_chunk_find_const
|
||||
to find the desired chunk type, and if non null use lodepng_inspect_chunk (with
|
||||
chunk_pointer - start_of_file as pos).
|
||||
Supports most metadata chunks from the PNG standard (gAMA, bKGD, tEXt, ...).
|
||||
Ignores unsupported, unknown, non-metadata or IHDR chunks (without error).
|
||||
Requirements: &in[pos] must point to start of a chunk, must use regular
|
||||
lodepng_inspect first since format of most other chunks depends on IHDR, and if
|
||||
there is a PLTE chunk, that one must be inspected before tRNS or bKGD.
|
||||
*/
|
||||
unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos,
|
||||
const unsigned char* in, size_t insize);
|
||||
|
||||
#ifdef LODEPNG_COMPILE_ENCODER
|
||||
/*This function allocates the out buffer with standard malloc and stores the size in *outsize.*/
|
||||
@@ -674,11 +800,23 @@ The lodepng_chunk functions are normally not needed, except to traverse the
|
||||
unknown chunks stored in the LodePNGInfo struct, or add new ones to it.
|
||||
It also allows traversing the chunks of an encoded PNG file yourself.
|
||||
|
||||
PNG standard chunk naming conventions:
|
||||
First byte: uppercase = critical, lowercase = ancillary
|
||||
Second byte: uppercase = public, lowercase = private
|
||||
Third byte: must be uppercase
|
||||
Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy
|
||||
The chunk pointer always points to the beginning of the chunk itself, that is
|
||||
the first byte of the 4 length bytes.
|
||||
|
||||
In the PNG file format, chunks have the following format:
|
||||
-4 bytes length: length of the data of the chunk in bytes (chunk itself is 12 bytes longer)
|
||||
-4 bytes chunk type (ASCII a-z,A-Z only, see below)
|
||||
-length bytes of data (may be 0 bytes if length was 0)
|
||||
-4 bytes of CRC, computed on chunk name + data
|
||||
|
||||
The first chunk starts at the 8th byte of the PNG file, the entire rest of the file
|
||||
exists out of concatenated chunks with the above format.
|
||||
|
||||
PNG standard chunk ASCII naming conventions:
|
||||
-First byte: uppercase = critical, lowercase = ancillary
|
||||
-Second byte: uppercase = public, lowercase = private
|
||||
-Third byte: must be uppercase
|
||||
-Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -713,24 +851,38 @@ unsigned lodepng_chunk_check_crc(const unsigned char* chunk);
|
||||
/*generates the correct CRC from the data and puts it in the last 4 bytes of the chunk*/
|
||||
void lodepng_chunk_generate_crc(unsigned char* chunk);
|
||||
|
||||
/*iterate to next chunks. don't use on IEND chunk, as there is no next chunk then*/
|
||||
unsigned char* lodepng_chunk_next(unsigned char* chunk);
|
||||
const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk);
|
||||
/*
|
||||
Iterate to next chunks, allows iterating through all chunks of the PNG file.
|
||||
Input must be at the beginning of a chunk (result of a previous lodepng_chunk_next call,
|
||||
or the 8th byte of a PNG file which always has the first chunk), or alternatively may
|
||||
point to the first byte of the PNG file (which is not a chunk but the magic header, the
|
||||
function will then skip over it and return the first real chunk).
|
||||
Will output pointer to the start of the next chunk, or at or beyond end of the file if there
|
||||
is no more chunk after this or possibly if the chunk is corrupt.
|
||||
Start this process at the 8th byte of the PNG file.
|
||||
In a non-corrupt PNG file, the last chunk should have name "IEND".
|
||||
*/
|
||||
unsigned char* lodepng_chunk_next(unsigned char* chunk, unsigned char* end);
|
||||
const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end);
|
||||
|
||||
/*Finds the first chunk with the given type in the range [chunk, end), or returns NULL if not found.*/
|
||||
unsigned char* lodepng_chunk_find(unsigned char* chunk, unsigned char* end, const char type[5]);
|
||||
const unsigned char* lodepng_chunk_find_const(const unsigned char* chunk, const unsigned char* end, const char type[5]);
|
||||
|
||||
/*
|
||||
Appends chunk to the data in out. The given chunk should already have its chunk header.
|
||||
The out variable and outlength are updated to reflect the new reallocated buffer.
|
||||
The out variable and outsize are updated to reflect the new reallocated buffer.
|
||||
Returns error code (0 if it went ok)
|
||||
*/
|
||||
unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk);
|
||||
unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk);
|
||||
|
||||
/*
|
||||
Appends new chunk to out. The chunk to append is given by giving its length, type
|
||||
and data separately. The type is a 4-letter string.
|
||||
The out variable and outlength are updated to reflect the new reallocated buffer.
|
||||
The out variable and outsize are updated to reflect the new reallocated buffer.
|
||||
Returne error code (0 if it went ok)
|
||||
*/
|
||||
unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length,
|
||||
unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, unsigned length,
|
||||
const char* type, const unsigned char* data);
|
||||
|
||||
|
||||
@@ -814,15 +966,13 @@ unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const
|
||||
|
||||
#ifdef LODEPNG_COMPILE_CPP
|
||||
/* The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers. */
|
||||
namespace lodepng
|
||||
{
|
||||
namespace lodepng {
|
||||
#ifdef LODEPNG_COMPILE_PNG
|
||||
class State : public LodePNGState
|
||||
{
|
||||
class State : public LodePNGState {
|
||||
public:
|
||||
State();
|
||||
State(const State& other);
|
||||
virtual ~State();
|
||||
~State();
|
||||
State& operator=(const State& other);
|
||||
};
|
||||
|
||||
@@ -890,16 +1040,21 @@ TODO:
|
||||
[.] test if there are no memory leaks or security exploits - done a lot but needs to be checked often
|
||||
[.] check compatibility with various compilers - done but needs to be redone for every newer version
|
||||
[X] converting color to 16-bit per channel types
|
||||
[ ] read all public PNG chunk types (but never let the color profile and gamma ones touch RGB values)
|
||||
[X] support color profile chunk types (but never let them touch RGB values by default)
|
||||
[ ] support all public PNG chunk types (almost done except sBIT, sPLT and hIST)
|
||||
[ ] make sure encoder generates no chunks with size > (2^31)-1
|
||||
[ ] partial decoding (stream processing)
|
||||
[X] let the "isFullyOpaque" function check color keys and transparent palettes too
|
||||
[X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl"
|
||||
[ ] don't stop decoding on errors like 69, 57, 58 (make warnings)
|
||||
[ ] allow treating some errors like warnings, when image is recoverable (e.g. 69, 57, 58)
|
||||
[ ] make warnings like: oob palette, checksum fail, data after iend, wrong/unknown crit chunk, no null terminator in text, ...
|
||||
[ ] error messages with line numbers (and version)
|
||||
[ ] errors in state instead of as return code?
|
||||
[ ] new errors/warnings like suspiciously big decompressed ztxt or iccp chunk
|
||||
[ ] let the C++ wrapper catch exceptions coming from the standard library and return LodePNG error codes
|
||||
[ ] allow user to provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ...
|
||||
[ ] allow user to give data (void*) to custom allocator
|
||||
[X] provide alternatives for C library functions not present on some platforms (memcpy, ...)
|
||||
*/
|
||||
|
||||
#endif /*LODEPNG_H inclusion guard*/
|
||||
@@ -994,8 +1149,10 @@ The following features are supported by the decoder:
|
||||
*) zlib decompression (inflate)
|
||||
*) zlib compression (deflate)
|
||||
*) CRC32 and ADLER32 checksums
|
||||
*) colorimetric color profile conversions: currently experimentally available in lodepng_util.cpp only,
|
||||
plus alternatively ability to pass on chroma/gamma/ICC profile information to other color management system.
|
||||
*) handling of unknown chunks, allowing making a PNG editor that stores custom and unknown chunks.
|
||||
*) the following chunks are supported (generated/interpreted) by both encoder and decoder:
|
||||
*) the following chunks are supported by both encoder and decoder:
|
||||
IHDR: header information
|
||||
PLTE: color palette
|
||||
IDAT: pixel data
|
||||
@@ -1007,6 +1164,10 @@ The following features are supported by the decoder:
|
||||
bKGD: suggested background color
|
||||
pHYs: physical dimensions
|
||||
tIME: modification time
|
||||
cHRM: RGB chromaticities
|
||||
gAMA: RGB gamma correction
|
||||
iCCP: ICC color profile
|
||||
sRGB: rendering intent
|
||||
|
||||
1.2. features not supported
|
||||
---------------------------
|
||||
@@ -1015,10 +1176,10 @@ The following features are _not_ supported:
|
||||
|
||||
*) some features needed to make a conformant PNG-Editor might be still missing.
|
||||
*) partial loading/stream processing. All data must be available and is processed in one call.
|
||||
*) The following public chunks are not supported but treated as unknown chunks by LodePNG
|
||||
cHRM, gAMA, iCCP, sRGB, sBIT, hIST, sPLT
|
||||
Some of these are not supported on purpose: LodePNG wants to provide the RGB values
|
||||
stored in the pixels, not values modified by system dependent gamma or color models.
|
||||
*) The following public chunks are not (yet) supported but treated as unknown chunks by LodePNG:
|
||||
sBIT
|
||||
hIST
|
||||
sPLT
|
||||
|
||||
|
||||
2. C and C++ version
|
||||
@@ -1092,7 +1253,7 @@ LodePNGColorMode info_raw
|
||||
When decoding, here you can specify which color type you want
|
||||
the resulting raw image to be. If this is different from the colortype of the
|
||||
PNG, then the decoder will automatically convert the result. This conversion
|
||||
always works, except if you want it to convert a color PNG to greyscale or to
|
||||
always works, except if you want it to convert a color PNG to grayscale or to
|
||||
a palette with missing colors.
|
||||
|
||||
By default, 32-bit color is used for the result.
|
||||
@@ -1188,7 +1349,7 @@ can encode the colors of all pixels without information loss.
|
||||
An important thing to note about LodePNG, is that the color type of the PNG, and
|
||||
the color type of the raw image, are completely independent. By default, when
|
||||
you decode a PNG, you get the result as a raw image in the color type you want,
|
||||
no matter whether the PNG was encoded with a palette, greyscale or RGBA color.
|
||||
no matter whether the PNG was encoded with a palette, grayscale or RGBA color.
|
||||
And if you encode an image, by default LodePNG will automatically choose the PNG
|
||||
color type that gives good compression based on the values of colors and amount
|
||||
of colors in the image. It can be configured to let you control it instead as
|
||||
@@ -1196,10 +1357,10 @@ well, though.
|
||||
|
||||
To be able to do this, LodePNG does conversions from one color mode to another.
|
||||
It can convert from almost any color type to any other color type, except the
|
||||
following conversions: RGB to greyscale is not supported, and converting to a
|
||||
following conversions: RGB to grayscale is not supported, and converting to a
|
||||
palette when the palette doesn't have a required color is not supported. This is
|
||||
not supported on purpose: this is information loss which requires a color
|
||||
reduction algorithm that is beyong the scope of a PNG encoder (yes, RGB to grey
|
||||
reduction algorithm that is beyond the scope of a PNG encoder (yes, RGB to gray
|
||||
is easy, but there are multiple ways if you want to give some channels more
|
||||
weight).
|
||||
|
||||
@@ -1220,10 +1381,10 @@ decoding to have another color type, a conversion is done by LodePNG.
|
||||
|
||||
The PNG specification gives the following color types:
|
||||
|
||||
0: greyscale, bit depths 1, 2, 4, 8, 16
|
||||
0: grayscale, bit depths 1, 2, 4, 8, 16
|
||||
2: RGB, bit depths 8 and 16
|
||||
3: palette, bit depths 1, 2, 4 and 8
|
||||
4: greyscale with alpha, bit depths 8 and 16
|
||||
4: grayscale with alpha, bit depths 8 and 16
|
||||
6: RGBA, bit depths 8 and 16
|
||||
|
||||
Bit depth is the amount of bits per pixel per color channel. So the total amount
|
||||
@@ -1272,15 +1433,22 @@ To avoid some confusion:
|
||||
the raw image correctly before encoding.
|
||||
-both encoder and decoder use the same color converter.
|
||||
|
||||
The function lodepng_convert does the color conversion. It is available in the
|
||||
interface but normally isn't needed since the encoder and decoder already call
|
||||
it.
|
||||
|
||||
Non supported color conversions:
|
||||
-color to greyscale: no error is thrown, but the result will look ugly because
|
||||
only the red channel is taken
|
||||
-anything to palette when that palette does not have that color in it: in this
|
||||
case an error is thrown
|
||||
-color to grayscale when non-gray pixels are present: no error is thrown, but
|
||||
the result will look ugly because only the red channel is taken (it assumes all
|
||||
three channels are the same in this case so ignores green and blue). The reason
|
||||
no error is given is to allow converting from three-channel grayscale images to
|
||||
one-channel even if there are numerical imprecisions.
|
||||
-anything to palette when the palette does not have an exact match for a from-color
|
||||
in it: in this case an error is thrown
|
||||
|
||||
Supported color conversions:
|
||||
-anything to 8-bit RGB, 8-bit RGBA, 16-bit RGB, 16-bit RGBA
|
||||
-any grey or grey+alpha, to grey or grey+alpha
|
||||
-any gray or gray+alpha, to gray or gray+alpha
|
||||
-anything to a palette, as long as the palette has the requested colors in it
|
||||
-removing alpha channel
|
||||
-higher to smaller bitdepth, and vice versa
|
||||
@@ -1293,10 +1461,6 @@ false.
|
||||
as the PNG has, by setting the color_convert setting to false. Settings in
|
||||
info_raw are then ignored.
|
||||
|
||||
The function lodepng_convert does the color conversion. It is available in the
|
||||
interface but normally isn't needed since the encoder and decoder already call
|
||||
it.
|
||||
|
||||
6.3. padding bits
|
||||
-----------------
|
||||
|
||||
@@ -1305,7 +1469,7 @@ have a bit amount that isn't a multiple of 8, then padding bits are used so that
|
||||
scanline starts at a fresh byte. But that is NOT true for the LodePNG raw input and output.
|
||||
The raw input image you give to the encoder, and the raw output image you get from the decoder
|
||||
will NOT have these padding bits, e.g. in the case of a 1-bit image with a width
|
||||
of 7 pixels, the first pixel of the second scanline will the the 8th bit of the first byte,
|
||||
of 7 pixels, the first pixel of the second scanline will the 8th bit of the first byte,
|
||||
not the first bit of a new byte.
|
||||
|
||||
6.4. A note about 16-bits per channel and endianness
|
||||
@@ -1406,12 +1570,12 @@ Iterate to the next chunk. This works if you have a buffer with consecutive chun
|
||||
functions do no boundary checking of the allocated data whatsoever, so make sure there is enough
|
||||
data available in the buffer to be able to go to the next chunk.
|
||||
|
||||
unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk):
|
||||
unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length,
|
||||
unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk):
|
||||
unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, unsigned length,
|
||||
const char* type, const unsigned char* data):
|
||||
|
||||
These functions are used to create new chunks that are appended to the data in *out that has
|
||||
length *outlength. The append function appends an existing chunk to the new data. The create
|
||||
length *outsize. The append function appends an existing chunk to the new data. The create
|
||||
function creates a new chunk with the given parameters and appends it. Type is the 4-letter
|
||||
name of the chunk.
|
||||
|
||||
@@ -1422,7 +1586,7 @@ The LodePNGInfo struct contains fields with the unknown chunk in it. It has 3
|
||||
buffers (each with size) to contain 3 types of unknown chunks:
|
||||
the ones that come before the PLTE chunk, the ones that come between the PLTE
|
||||
and the IDAT chunks, and the ones that come after the IDAT chunks.
|
||||
It's necessary to make the distionction between these 3 cases because the PNG
|
||||
It's necessary to make the distinction between these 3 cases because the PNG
|
||||
standard forces to keep the ordering of unknown chunks compared to the critical
|
||||
chunks, but does not force any other ordering rules.
|
||||
|
||||
@@ -1505,7 +1669,7 @@ C and C++.
|
||||
*) Other Compilers
|
||||
|
||||
If you encounter problems on any compilers, feel free to let me know and I may
|
||||
try to fix it if the compiler is modern and standards complient.
|
||||
try to fix it if the compiler is modern and standards compliant.
|
||||
|
||||
|
||||
10. examples
|
||||
@@ -1520,8 +1684,7 @@ examples can be found on the LodePNG website.
|
||||
#include "lodepng.h"
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int main(int argc, char *argv[]) {
|
||||
const char* filename = argc > 1 ? argv[1] : "test.png";
|
||||
|
||||
//load and decode
|
||||
@@ -1540,8 +1703,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
#include "lodepng.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int main(int argc, char *argv[]) {
|
||||
unsigned error;
|
||||
unsigned char* image;
|
||||
size_t width, height;
|
||||
@@ -1567,6 +1729,8 @@ For decoding:
|
||||
state.decoder.zlibsettings.ignore_adler32: ignore ADLER32 checksums
|
||||
state.decoder.zlibsettings.custom_...: use custom inflate function
|
||||
state.decoder.ignore_crc: ignore CRC checksums
|
||||
state.decoder.ignore_critical: ignore unknown critical chunks
|
||||
state.decoder.ignore_end: ignore missing IEND chunk. May fail if this corruption causes other errors
|
||||
state.decoder.color_convert: convert internal PNG color to chosen one
|
||||
state.decoder.read_text_chunks: whether to read in text metadata chunks
|
||||
state.decoder.remember_unknown_chunks: whether to read in unknown chunks
|
||||
@@ -1608,6 +1772,24 @@ yyyymmdd.
|
||||
Some changes aren't backwards compatible. Those are indicated with a (!)
|
||||
symbol.
|
||||
|
||||
Not all changes are listed here, the commit history in github lists more:
|
||||
https://github.com/lvandeve/lodepng
|
||||
|
||||
*) 06 mar 2020: simplified some of the dynamic memory allocations.
|
||||
*) 12 jan 2020: (!) added 'end' argument to lodepng_chunk_next to allow correct
|
||||
overflow checks.
|
||||
*) 14 aug 2019: around 25% faster decoding thanks to huffman lookup tables.
|
||||
*) 15 jun 2019: (!) auto_choose_color API changed (for bugfix: don't use palette
|
||||
if gray ICC profile) and non-ICC LodePNGColorProfile renamed to
|
||||
LodePNGColorStats.
|
||||
*) 30 dec 2018: code style changes only: removed newlines before opening braces.
|
||||
*) 10 sep 2018: added way to inspect metadata chunks without full decoding.
|
||||
*) 19 aug 2018: (!) fixed color mode bKGD is encoded with and made it use
|
||||
palette index in case of palette.
|
||||
*) 10 aug 2018: (!) added support for gAMA, cHRM, sRGB and iCCP chunks. This
|
||||
change is backwards compatible unless you relied on unknown_chunks for those.
|
||||
*) 11 jun 2018: less restrictive check for pixel size integer overflow
|
||||
*) 14 jan 2018: allow optionally ignoring a few more recoverable errors
|
||||
*) 17 sep 2017: fix memory leak for some encoder input error cases
|
||||
*) 27 nov 2016: grey+alpha auto color model detection bugfix
|
||||
*) 18 apr 2016: Changed qsort to custom stable sort (for platforms w/o qsort).
|
||||
@@ -1616,6 +1798,7 @@ symbol.
|
||||
*) 08 dec 2015: Made load_file function return error if file can't be opened.
|
||||
*) 24 okt 2015: Bugfix with decoding to palette output.
|
||||
*) 18 apr 2015: Boundary PM instead of just package-merge for faster encoding.
|
||||
*) 24 aug 2014: Moved to github
|
||||
*) 23 aug 2014: Reduced needless memory usage of decoder.
|
||||
*) 28 jun 2014: Removed fix_png setting, always support palette OOB for
|
||||
simplicity. Made ColorProfile public.
|
||||
@@ -1623,25 +1806,25 @@ symbol.
|
||||
*) 22 dec 2013: Power of two windowsize required for optimization.
|
||||
*) 15 apr 2013: Fixed bug with LAC_ALPHA and color key.
|
||||
*) 25 mar 2013: Added an optional feature to ignore some PNG errors (fix_png).
|
||||
*) 11 mar 2013 (!): Bugfix with custom free. Changed from "my" to "lodepng_"
|
||||
*) 11 mar 2013: (!) Bugfix with custom free. Changed from "my" to "lodepng_"
|
||||
prefix for the custom allocators and made it possible with a new #define to
|
||||
use custom ones in your project without needing to change lodepng's code.
|
||||
*) 28 jan 2013: Bugfix with color key.
|
||||
*) 27 okt 2012: Tweaks in text chunk keyword length error handling.
|
||||
*) 8 okt 2012 (!): Added new filter strategy (entropy) and new auto color mode.
|
||||
*) 8 okt 2012: (!) Added new filter strategy (entropy) and new auto color mode.
|
||||
(no palette). Better deflate tree encoding. New compression tweak settings.
|
||||
Faster color conversions while decoding. Some internal cleanups.
|
||||
*) 23 sep 2012: Reduced warnings in Visual Studio a little bit.
|
||||
*) 1 sep 2012 (!): Removed #define's for giving custom (de)compression functions
|
||||
*) 1 sep 2012: (!) Removed #define's for giving custom (de)compression functions
|
||||
and made it work with function pointers instead.
|
||||
*) 23 jun 2012: Added more filter strategies. Made it easier to use custom alloc
|
||||
and free functions and toggle #defines from compiler flags. Small fixes.
|
||||
*) 6 may 2012 (!): Made plugging in custom zlib/deflate functions more flexible.
|
||||
*) 22 apr 2012 (!): Made interface more consistent, renaming a lot. Removed
|
||||
*) 6 may 2012: (!) Made plugging in custom zlib/deflate functions more flexible.
|
||||
*) 22 apr 2012: (!) Made interface more consistent, renaming a lot. Removed
|
||||
redundant C++ codec classes. Reduced amount of structs. Everything changed,
|
||||
but it is cleaner now imho and functionality remains the same. Also fixed
|
||||
several bugs and shrunk the implementation code. Made new samples.
|
||||
*) 6 nov 2011 (!): By default, the encoder now automatically chooses the best
|
||||
*) 6 nov 2011: (!) By default, the encoder now automatically chooses the best
|
||||
PNG color model and bit depth, based on the amount and type of colors of the
|
||||
raw image. For this, autoLeaveOutAlphaChannel replaced by auto_choose_color.
|
||||
*) 9 okt 2011: simpler hash chain implementation for the encoder.
|
||||
@@ -1650,7 +1833,7 @@ symbol.
|
||||
A bug with the PNG filtertype heuristic was fixed, so that it chooses much
|
||||
better ones (it's quite significant). A setting to do an experimental, slow,
|
||||
brute force search for PNG filter types is added.
|
||||
*) 17 aug 2011 (!): changed some C zlib related function names.
|
||||
*) 17 aug 2011: (!) changed some C zlib related function names.
|
||||
*) 16 aug 2011: made the code less wide (max 120 characters per line).
|
||||
*) 17 apr 2011: code cleanup. Bugfixes. Convert low to 16-bit per sample colors.
|
||||
*) 21 feb 2011: fixed compiling for C90. Fixed compiling with sections disabled.
|
||||
@@ -1758,5 +1941,5 @@ Domain: gmail dot com.
|
||||
Account: lode dot vandevenne.
|
||||
|
||||
|
||||
Copyright (c) 2005-2017 Lode Vandevenne
|
||||
Copyright (c) 2005-2020 Lode Vandevenne
|
||||
*/
|
||||
|
||||
+197
-88
@@ -1,4 +1,4 @@
|
||||
/* stb_image - v2.22 - public domain image loader - http://nothings.org/stb
|
||||
/* stb_image - v2.25 - public domain image loader - http://nothings.org/stb
|
||||
no warranty implied; use at your own risk
|
||||
|
||||
Do this:
|
||||
@@ -48,9 +48,12 @@ LICENSE
|
||||
|
||||
RECENT REVISION HISTORY:
|
||||
|
||||
2.25 (2020-02-02) fix warnings
|
||||
2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically
|
||||
2.23 (2019-08-11) fix clang static analysis warning
|
||||
2.22 (2019-03-04) gif fixes, fix warnings
|
||||
2.21 (2019-02-25) fix typo in comment
|
||||
2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs
|
||||
2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs
|
||||
2.19 (2018-02-11) fix warning
|
||||
2.18 (2018-01-30) fix warnings
|
||||
2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings
|
||||
@@ -104,7 +107,8 @@ RECENT REVISION HISTORY:
|
||||
Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus
|
||||
Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo
|
||||
Christian Floisand Kevin Schmidt JR Smith github:darealshinji
|
||||
Blazej Dariusz Roszkowski github:Michaelangel007
|
||||
Brad Weinberger Matvey Cherevko github:Michaelangel007
|
||||
Blazej Dariusz Roszkowski Alexander Veselov
|
||||
*/
|
||||
|
||||
#ifndef STBI_INCLUDE_STB_IMAGE_H
|
||||
@@ -433,7 +437,7 @@ STBIDEF int stbi_is_hdr_from_file(FILE *f);
|
||||
|
||||
|
||||
// get a VERY brief reason for failure
|
||||
// NOT THREADSAFE
|
||||
// on most compilers (and ALL modern mainstream compilers) this is threadsafe
|
||||
STBIDEF const char *stbi_failure_reason (void);
|
||||
|
||||
// free the loaded image -- this is just free()
|
||||
@@ -466,6 +470,11 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert);
|
||||
// flip the image vertically, so the first pixel in the output array is the bottom left
|
||||
STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);
|
||||
|
||||
// as above, but only applies to images loaded on the thread that calls the function
|
||||
// this function is only available if your compiler supports thread-local variables;
|
||||
// calling it will fail to link if your compiler doesn't
|
||||
STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip);
|
||||
|
||||
// ZLIB client - used by PNG, available for other purposes
|
||||
|
||||
STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen);
|
||||
@@ -562,6 +571,17 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch
|
||||
#define stbi_inline __forceinline
|
||||
#endif
|
||||
|
||||
#ifndef STBI_NO_THREAD_LOCALS
|
||||
#if defined(__cplusplus) && __cplusplus >= 201103L
|
||||
#define STBI_THREAD_LOCAL thread_local
|
||||
#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
#define STBI_THREAD_LOCAL _Thread_local
|
||||
#elif defined(__GNUC__)
|
||||
#define STBI_THREAD_LOCAL __thread
|
||||
#elif defined(_MSC_VER)
|
||||
#define STBI_THREAD_LOCAL __declspec(thread)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
typedef unsigned short stbi__uint16;
|
||||
@@ -872,19 +892,24 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int
|
||||
static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp);
|
||||
#endif
|
||||
|
||||
// this is not threadsafe
|
||||
static const char *stbi__g_failure_reason;
|
||||
static
|
||||
#ifdef STBI_THREAD_LOCAL
|
||||
STBI_THREAD_LOCAL
|
||||
#endif
|
||||
const char *stbi__g_failure_reason;
|
||||
|
||||
STBIDEF const char *stbi_failure_reason(void)
|
||||
{
|
||||
return stbi__g_failure_reason;
|
||||
}
|
||||
|
||||
#ifndef STBI_NO_FAILURE_STRINGS
|
||||
static int stbi__err(const char *str)
|
||||
{
|
||||
stbi__g_failure_reason = str;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void *stbi__malloc(size_t size)
|
||||
{
|
||||
@@ -923,11 +948,13 @@ static int stbi__mul2sizes_valid(int a, int b)
|
||||
return a <= INT_MAX/b;
|
||||
}
|
||||
|
||||
#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR)
|
||||
// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow
|
||||
static int stbi__mad2sizes_valid(int a, int b, int add)
|
||||
{
|
||||
return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add);
|
||||
}
|
||||
#endif
|
||||
|
||||
// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow
|
||||
static int stbi__mad3sizes_valid(int a, int b, int c, int add)
|
||||
@@ -945,12 +972,14 @@ static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR)
|
||||
// mallocs with size overflow checking
|
||||
static void *stbi__malloc_mad2(int a, int b, int add)
|
||||
{
|
||||
if (!stbi__mad2sizes_valid(a, b, add)) return NULL;
|
||||
return stbi__malloc(a*b + add);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void *stbi__malloc_mad3(int a, int b, int c, int add)
|
||||
{
|
||||
@@ -994,13 +1023,29 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp);
|
||||
static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp);
|
||||
#endif
|
||||
|
||||
static int stbi__vertically_flip_on_load = 0;
|
||||
static int stbi__vertically_flip_on_load_global = 0;
|
||||
|
||||
STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip)
|
||||
{
|
||||
stbi__vertically_flip_on_load = flag_true_if_should_flip;
|
||||
stbi__vertically_flip_on_load_global = flag_true_if_should_flip;
|
||||
}
|
||||
|
||||
#ifndef STBI_THREAD_LOCAL
|
||||
#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global
|
||||
#else
|
||||
static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set;
|
||||
|
||||
STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip)
|
||||
{
|
||||
stbi__vertically_flip_on_load_local = flag_true_if_should_flip;
|
||||
stbi__vertically_flip_on_load_set = 1;
|
||||
}
|
||||
|
||||
#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \
|
||||
? stbi__vertically_flip_on_load_local \
|
||||
: stbi__vertically_flip_on_load_global)
|
||||
#endif // STBI_THREAD_LOCAL
|
||||
|
||||
static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc)
|
||||
{
|
||||
memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields
|
||||
@@ -1022,6 +1067,8 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re
|
||||
#endif
|
||||
#ifndef STBI_NO_PSD
|
||||
if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc);
|
||||
#else
|
||||
STBI_NOTUSED(bpc);
|
||||
#endif
|
||||
#ifndef STBI_NO_PIC
|
||||
if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri);
|
||||
@@ -1110,8 +1157,8 @@ static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int byt
|
||||
|
||||
stbi_uc *bytes = (stbi_uc *)image;
|
||||
for (slice = 0; slice < z; ++slice) {
|
||||
stbi__vertical_flip(bytes, w, h, bytes_per_pixel);
|
||||
bytes += slice_size;
|
||||
stbi__vertical_flip(bytes, w, h, bytes_per_pixel);
|
||||
bytes += slice_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1197,7 +1244,7 @@ static FILE *stbi__fopen(char const *filename, char const *mode)
|
||||
wchar_t wFilename[1024];
|
||||
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)))
|
||||
return 0;
|
||||
|
||||
|
||||
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)))
|
||||
return 0;
|
||||
|
||||
@@ -1299,15 +1346,15 @@ STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *u
|
||||
STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
|
||||
{
|
||||
unsigned char *result;
|
||||
stbi__context s;
|
||||
stbi__start_mem(&s,buffer,len);
|
||||
|
||||
stbi__context s;
|
||||
stbi__start_mem(&s,buffer,len);
|
||||
|
||||
result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp);
|
||||
if (stbi__vertically_flip_on_load) {
|
||||
stbi__vertical_flip_slices( result, *x, *y, *z, *comp );
|
||||
stbi__vertical_flip_slices( result, *x, *y, *z, *comp );
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1476,6 +1523,9 @@ stbi_inline static stbi_uc stbi__get8(stbi__context *s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)
|
||||
// nothing
|
||||
#else
|
||||
stbi_inline static int stbi__at_eof(stbi__context *s)
|
||||
{
|
||||
if (s->io.read) {
|
||||
@@ -1487,7 +1537,11 @@ stbi_inline static int stbi__at_eof(stbi__context *s)
|
||||
|
||||
return s->img_buffer >= s->img_buffer_end;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC)
|
||||
// nothing
|
||||
#else
|
||||
static void stbi__skip(stbi__context *s, int n)
|
||||
{
|
||||
if (n < 0) {
|
||||
@@ -1504,7 +1558,11 @@ static void stbi__skip(stbi__context *s, int n)
|
||||
}
|
||||
s->img_buffer += n;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM)
|
||||
// nothing
|
||||
#else
|
||||
static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n)
|
||||
{
|
||||
if (s->io.read) {
|
||||
@@ -1528,18 +1586,27 @@ static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n)
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC)
|
||||
// nothing
|
||||
#else
|
||||
static int stbi__get16be(stbi__context *s)
|
||||
{
|
||||
int z = stbi__get8(s);
|
||||
return (z << 8) + stbi__get8(s);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC)
|
||||
// nothing
|
||||
#else
|
||||
static stbi__uint32 stbi__get32be(stbi__context *s)
|
||||
{
|
||||
stbi__uint32 z = stbi__get16be(s);
|
||||
return (z << 16) + stbi__get16be(s);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF)
|
||||
// nothing
|
||||
@@ -1561,7 +1628,9 @@ static stbi__uint32 stbi__get32le(stbi__context *s)
|
||||
|
||||
#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings
|
||||
|
||||
|
||||
#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)
|
||||
// nothing
|
||||
#else
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// generic converter from built-in img_n to req_comp
|
||||
@@ -1577,7 +1646,11 @@ static stbi_uc stbi__compute_y(int r, int g, int b)
|
||||
{
|
||||
return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)
|
||||
// nothing
|
||||
#else
|
||||
static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y)
|
||||
{
|
||||
int i,j;
|
||||
@@ -1621,12 +1694,20 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r
|
||||
STBI_FREE(data);
|
||||
return good;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD)
|
||||
// nothing
|
||||
#else
|
||||
static stbi__uint16 stbi__compute_y_16(int r, int g, int b)
|
||||
{
|
||||
return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD)
|
||||
// nothing
|
||||
#else
|
||||
static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y)
|
||||
{
|
||||
int i,j;
|
||||
@@ -1670,6 +1751,7 @@ static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int r
|
||||
STBI_FREE(data);
|
||||
return good;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef STBI_NO_LINEAR
|
||||
static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp)
|
||||
@@ -4941,6 +5023,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
|
||||
++s->img_n;
|
||||
}
|
||||
STBI_FREE(z->expanded); z->expanded = NULL;
|
||||
// end of PNG chunk, read and skip CRC
|
||||
stbi__get32be(s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -5079,7 +5163,7 @@ static int stbi__high_bit(unsigned int z)
|
||||
if (z >= 0x00100) { n += 8; z >>= 8; }
|
||||
if (z >= 0x00010) { n += 4; z >>= 4; }
|
||||
if (z >= 0x00004) { n += 2; z >>= 2; }
|
||||
if (z >= 0x00002) { n += 1; z >>= 1; }
|
||||
if (z >= 0x00002) { n += 1;/* >>= 1;*/ }
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -5110,7 +5194,7 @@ static int stbi__shiftsigned(unsigned int v, int shift, int bits)
|
||||
v <<= -shift;
|
||||
else
|
||||
v >>= shift;
|
||||
STBI_ASSERT(v >= 0 && v < 256);
|
||||
STBI_ASSERT(v < 256);
|
||||
v >>= (8-bits);
|
||||
STBI_ASSERT(bits >= 0 && bits <= 8);
|
||||
return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits];
|
||||
@@ -5120,6 +5204,7 @@ typedef struct
|
||||
{
|
||||
int bpp, offset, hsz;
|
||||
unsigned int mr,mg,mb,ma, all_a;
|
||||
int extra_read;
|
||||
} stbi__bmp_data;
|
||||
|
||||
static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
|
||||
@@ -5132,6 +5217,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
|
||||
info->offset = stbi__get32le(s);
|
||||
info->hsz = hsz = stbi__get32le(s);
|
||||
info->mr = info->mg = info->mb = info->ma = 0;
|
||||
info->extra_read = 14;
|
||||
|
||||
if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown");
|
||||
if (hsz == 12) {
|
||||
@@ -5175,6 +5261,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
|
||||
info->mr = stbi__get32le(s);
|
||||
info->mg = stbi__get32le(s);
|
||||
info->mb = stbi__get32le(s);
|
||||
info->extra_read += 12;
|
||||
// not documented, but generated by photoshop and handled by mspaint
|
||||
if (info->mr == info->mg && info->mg == info->mb) {
|
||||
// ?!?!?
|
||||
@@ -5231,13 +5318,19 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req
|
||||
|
||||
if (info.hsz == 12) {
|
||||
if (info.bpp < 24)
|
||||
psize = (info.offset - 14 - 24) / 3;
|
||||
psize = (info.offset - info.extra_read - 24) / 3;
|
||||
} else {
|
||||
if (info.bpp < 16)
|
||||
psize = (info.offset - 14 - info.hsz) >> 2;
|
||||
psize = (info.offset - info.extra_read - info.hsz) >> 2;
|
||||
}
|
||||
if (psize == 0) {
|
||||
STBI_ASSERT(info.offset == (s->img_buffer - s->buffer_start));
|
||||
}
|
||||
|
||||
s->img_n = ma ? 4 : 3;
|
||||
if (info.bpp == 24 && ma == 0xff000000)
|
||||
s->img_n = 3;
|
||||
else
|
||||
s->img_n = ma ? 4 : 3;
|
||||
if (req_comp && req_comp >= 3) // we can directly decode 3 or 4
|
||||
target = req_comp;
|
||||
else
|
||||
@@ -5259,7 +5352,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req
|
||||
if (info.hsz != 12) stbi__get8(s);
|
||||
pal[i][3] = 255;
|
||||
}
|
||||
stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4));
|
||||
stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4));
|
||||
if (info.bpp == 1) width = (s->img_x + 7) >> 3;
|
||||
else if (info.bpp == 4) width = (s->img_x + 1) >> 1;
|
||||
else if (info.bpp == 8) width = s->img_x;
|
||||
@@ -5308,7 +5401,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req
|
||||
int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0;
|
||||
int z = 0;
|
||||
int easy=0;
|
||||
stbi__skip(s, info.offset - 14 - info.hsz);
|
||||
stbi__skip(s, info.offset - info.extra_read - info.hsz);
|
||||
if (info.bpp == 24) width = 3 * s->img_x;
|
||||
else if (info.bpp == 16) width = 2*s->img_x;
|
||||
else /* bpp = 32 and pad = 0 */ width=0;
|
||||
@@ -5547,6 +5640,8 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req
|
||||
int RLE_repeating = 0;
|
||||
int read_next_pixel = 1;
|
||||
STBI_NOTUSED(ri);
|
||||
STBI_NOTUSED(tga_x_origin); // @TODO
|
||||
STBI_NOTUSED(tga_y_origin); // @TODO
|
||||
|
||||
// do a tiny bit of precessing
|
||||
if ( tga_image_type >= 8 )
|
||||
@@ -5710,6 +5805,7 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req
|
||||
// Microsoft's C compilers happy... [8^(
|
||||
tga_palette_start = tga_palette_len = tga_palette_bits =
|
||||
tga_x_origin = tga_y_origin = 0;
|
||||
STBI_NOTUSED(tga_palette_start);
|
||||
// OK, done
|
||||
return tga_data;
|
||||
}
|
||||
@@ -6195,7 +6291,7 @@ typedef struct
|
||||
int w,h;
|
||||
stbi_uc *out; // output buffer (always 4 components)
|
||||
stbi_uc *background; // The current "background" as far as a gif is concerned
|
||||
stbi_uc *history;
|
||||
stbi_uc *history;
|
||||
int flags, bgindex, ratio, transparent, eflags;
|
||||
stbi_uc pal[256][4];
|
||||
stbi_uc lpal[256][4];
|
||||
@@ -6283,7 +6379,7 @@ static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp)
|
||||
static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code)
|
||||
{
|
||||
stbi_uc *p, *c;
|
||||
int idx;
|
||||
int idx;
|
||||
|
||||
// recurse to decode the prefixes, since the linked-list is backwards,
|
||||
// and working backwards through an interleaved image would be nasty
|
||||
@@ -6292,12 +6388,12 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code)
|
||||
|
||||
if (g->cur_y >= g->max_y) return;
|
||||
|
||||
idx = g->cur_x + g->cur_y;
|
||||
idx = g->cur_x + g->cur_y;
|
||||
p = &g->out[idx];
|
||||
g->history[idx / 4] = 1;
|
||||
g->history[idx / 4] = 1;
|
||||
|
||||
c = &g->color_table[g->codes[code].suffix * 4];
|
||||
if (c[3] > 128) { // don't render transparent pixels;
|
||||
if (c[3] > 128) { // don't render transparent pixels;
|
||||
p[0] = c[2];
|
||||
p[1] = c[1];
|
||||
p[2] = c[0];
|
||||
@@ -6406,14 +6502,14 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
|
||||
// two back is the image from two frames ago, used for a very specific disposal format
|
||||
static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back)
|
||||
{
|
||||
int dispose;
|
||||
int first_frame;
|
||||
int pi;
|
||||
int pcount;
|
||||
int dispose;
|
||||
int first_frame;
|
||||
int pi;
|
||||
int pcount;
|
||||
STBI_NOTUSED(req_comp);
|
||||
|
||||
// on first frame, any non-written pixels get the background colour (non-transparent)
|
||||
first_frame = 0;
|
||||
first_frame = 0;
|
||||
if (g->out == 0) {
|
||||
if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header
|
||||
if (!stbi__mad3sizes_valid(4, g->w, g->h, 0))
|
||||
@@ -6425,17 +6521,17 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
|
||||
if (!g->out || !g->background || !g->history)
|
||||
return stbi__errpuc("outofmem", "Out of memory");
|
||||
|
||||
// image is treated as "transparent" at the start - ie, nothing overwrites the current background;
|
||||
// image is treated as "transparent" at the start - ie, nothing overwrites the current background;
|
||||
// background colour is only used for pixels that are not rendered first frame, after that "background"
|
||||
// color refers to the color that was there the previous frame.
|
||||
// color refers to the color that was there the previous frame.
|
||||
memset(g->out, 0x00, 4 * pcount);
|
||||
memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent)
|
||||
memset(g->history, 0x00, pcount); // pixels that were affected previous frame
|
||||
first_frame = 1;
|
||||
first_frame = 1;
|
||||
} else {
|
||||
// second frame - how do we dispoase of the previous one?
|
||||
dispose = (g->eflags & 0x1C) >> 2;
|
||||
pcount = g->w * g->h;
|
||||
dispose = (g->eflags & 0x1C) >> 2;
|
||||
pcount = g->w * g->h;
|
||||
|
||||
if ((dispose == 3) && (two_back == 0)) {
|
||||
dispose = 2; // if I don't have an image to revert back to, default to the old background
|
||||
@@ -6444,32 +6540,32 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
|
||||
if (dispose == 3) { // use previous graphic
|
||||
for (pi = 0; pi < pcount; ++pi) {
|
||||
if (g->history[pi]) {
|
||||
memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 );
|
||||
memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 );
|
||||
}
|
||||
}
|
||||
} else if (dispose == 2) {
|
||||
// restore what was changed last frame to background before that frame;
|
||||
} else if (dispose == 2) {
|
||||
// restore what was changed last frame to background before that frame;
|
||||
for (pi = 0; pi < pcount; ++pi) {
|
||||
if (g->history[pi]) {
|
||||
memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 );
|
||||
memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This is a non-disposal case eithe way, so just
|
||||
// This is a non-disposal case eithe way, so just
|
||||
// leave the pixels as is, and they will become the new background
|
||||
// 1: do not dispose
|
||||
// 0: not specified.
|
||||
}
|
||||
|
||||
// background is what out is after the undoing of the previou frame;
|
||||
memcpy( g->background, g->out, 4 * g->w * g->h );
|
||||
// background is what out is after the undoing of the previou frame;
|
||||
memcpy( g->background, g->out, 4 * g->w * g->h );
|
||||
}
|
||||
|
||||
// clear my history;
|
||||
// clear my history;
|
||||
memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame
|
||||
|
||||
for (;;) {
|
||||
int tag = stbi__get8(s);
|
||||
int tag = stbi__get8(s);
|
||||
switch (tag) {
|
||||
case 0x2C: /* Image Descriptor */
|
||||
{
|
||||
@@ -6514,19 +6610,19 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
|
||||
} else if (g->flags & 0x80) {
|
||||
g->color_table = (stbi_uc *) g->pal;
|
||||
} else
|
||||
return stbi__errpuc("missing color table", "Corrupt GIF");
|
||||
|
||||
return stbi__errpuc("missing color table", "Corrupt GIF");
|
||||
|
||||
o = stbi__process_gif_raster(s, g);
|
||||
if (!o) return NULL;
|
||||
|
||||
// if this was the first frame,
|
||||
pcount = g->w * g->h;
|
||||
// if this was the first frame,
|
||||
pcount = g->w * g->h;
|
||||
if (first_frame && (g->bgindex > 0)) {
|
||||
// if first frame, any pixel not drawn to gets the background color
|
||||
for (pi = 0; pi < pcount; ++pi) {
|
||||
if (g->history[pi] == 0) {
|
||||
g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be;
|
||||
memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 );
|
||||
g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be;
|
||||
memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6537,7 +6633,7 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
|
||||
case 0x21: // Comment Extension.
|
||||
{
|
||||
int len;
|
||||
int ext = stbi__get8(s);
|
||||
int ext = stbi__get8(s);
|
||||
if (ext == 0xF9) { // Graphic Control Extension.
|
||||
len = stbi__get8(s);
|
||||
if (len == 4) {
|
||||
@@ -6546,23 +6642,23 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
|
||||
|
||||
// unset old transparent
|
||||
if (g->transparent >= 0) {
|
||||
g->pal[g->transparent][3] = 255;
|
||||
}
|
||||
g->pal[g->transparent][3] = 255;
|
||||
}
|
||||
if (g->eflags & 0x01) {
|
||||
g->transparent = stbi__get8(s);
|
||||
if (g->transparent >= 0) {
|
||||
g->pal[g->transparent][3] = 0;
|
||||
g->pal[g->transparent][3] = 0;
|
||||
}
|
||||
} else {
|
||||
// don't need transparent
|
||||
stbi__skip(s, 1);
|
||||
g->transparent = -1;
|
||||
stbi__skip(s, 1);
|
||||
g->transparent = -1;
|
||||
}
|
||||
} else {
|
||||
stbi__skip(s, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while ((len = stbi__get8(s)) != 0) {
|
||||
stbi__skip(s, len);
|
||||
}
|
||||
@@ -6581,15 +6677,15 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
|
||||
static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
|
||||
{
|
||||
if (stbi__gif_test(s)) {
|
||||
int layers = 0;
|
||||
int layers = 0;
|
||||
stbi_uc *u = 0;
|
||||
stbi_uc *out = 0;
|
||||
stbi_uc *two_back = 0;
|
||||
stbi_uc *two_back = 0;
|
||||
stbi__gif g;
|
||||
int stride;
|
||||
int stride;
|
||||
memset(&g, 0, sizeof(g));
|
||||
if (delays) {
|
||||
*delays = 0;
|
||||
*delays = 0;
|
||||
}
|
||||
|
||||
do {
|
||||
@@ -6599,44 +6695,52 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y,
|
||||
if (u) {
|
||||
*x = g.w;
|
||||
*y = g.h;
|
||||
++layers;
|
||||
stride = g.w * g.h * 4;
|
||||
|
||||
++layers;
|
||||
stride = g.w * g.h * 4;
|
||||
|
||||
if (out) {
|
||||
out = (stbi_uc*) STBI_REALLOC( out, layers * stride );
|
||||
void *tmp = (stbi_uc*) STBI_REALLOC( out, layers * stride );
|
||||
if (NULL == tmp) {
|
||||
STBI_FREE(g.out);
|
||||
STBI_FREE(g.history);
|
||||
STBI_FREE(g.background);
|
||||
return stbi__errpuc("outofmem", "Out of memory");
|
||||
}
|
||||
else
|
||||
out = (stbi_uc*) tmp;
|
||||
if (delays) {
|
||||
*delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers );
|
||||
*delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers );
|
||||
}
|
||||
} else {
|
||||
out = (stbi_uc*)stbi__malloc( layers * stride );
|
||||
out = (stbi_uc*)stbi__malloc( layers * stride );
|
||||
if (delays) {
|
||||
*delays = (int*) stbi__malloc( layers * sizeof(int) );
|
||||
*delays = (int*) stbi__malloc( layers * sizeof(int) );
|
||||
}
|
||||
}
|
||||
memcpy( out + ((layers - 1) * stride), u, stride );
|
||||
memcpy( out + ((layers - 1) * stride), u, stride );
|
||||
if (layers >= 2) {
|
||||
two_back = out - 2 * stride;
|
||||
two_back = out - 2 * stride;
|
||||
}
|
||||
|
||||
if (delays) {
|
||||
(*delays)[layers - 1U] = g.delay;
|
||||
(*delays)[layers - 1U] = g.delay;
|
||||
}
|
||||
}
|
||||
} while (u != 0);
|
||||
} while (u != 0);
|
||||
|
||||
// free temp buffer;
|
||||
STBI_FREE(g.out);
|
||||
STBI_FREE(g.history);
|
||||
STBI_FREE(g.background);
|
||||
// free temp buffer;
|
||||
STBI_FREE(g.out);
|
||||
STBI_FREE(g.history);
|
||||
STBI_FREE(g.background);
|
||||
|
||||
// do the final conversion after loading everything;
|
||||
// do the final conversion after loading everything;
|
||||
if (req_comp && req_comp != 4)
|
||||
out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h);
|
||||
|
||||
*z = layers;
|
||||
*z = layers;
|
||||
return out;
|
||||
} else {
|
||||
return stbi__errpuc("not GIF", "Image was not as a gif type.");
|
||||
return stbi__errpuc("not GIF", "Image was not as a gif type.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6654,7 +6758,7 @@ static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req
|
||||
*y = g.h;
|
||||
|
||||
// moved conversion to after successful load so that the same
|
||||
// can be done for multiple frames.
|
||||
// can be done for multiple frames.
|
||||
if (req_comp && req_comp != 4)
|
||||
u = stbi__convert_format(u, 4, req_comp, g.w, g.h);
|
||||
} else if (g.out) {
|
||||
@@ -6662,9 +6766,9 @@ static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req
|
||||
STBI_FREE(g.out);
|
||||
}
|
||||
|
||||
// free buffers needed for multiple frame loading;
|
||||
// free buffers needed for multiple frame loading;
|
||||
STBI_FREE(g.history);
|
||||
STBI_FREE(g.background);
|
||||
STBI_FREE(g.background);
|
||||
|
||||
return u;
|
||||
}
|
||||
@@ -6936,7 +7040,12 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp)
|
||||
return 0;
|
||||
if (x) *x = s->img_x;
|
||||
if (y) *y = s->img_y;
|
||||
if (comp) *comp = info.ma ? 4 : 3;
|
||||
if (comp) {
|
||||
if (info.bpp == 24 && info.ma == 0xff000000)
|
||||
*comp = 3;
|
||||
else
|
||||
*comp = info.ma ? 4 : 3;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
@@ -7322,7 +7431,7 @@ STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user
|
||||
|
||||
/*
|
||||
revision history:
|
||||
2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs
|
||||
2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs
|
||||
2.19 (2018-02-11) fix warning
|
||||
2.18 (2018-01-30) fix warnings
|
||||
2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug
|
||||
|
||||
@@ -19,24 +19,210 @@
|
||||
**/
|
||||
|
||||
#include "Buffer.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
Buffer::Buffer(size_t size, BufferType type, vertex::Usage usage, uint32 mapflags)
|
||||
: size(size)
|
||||
, type(type)
|
||||
, usage(usage)
|
||||
, map_flags(mapflags)
|
||||
, is_mapped(false)
|
||||
love::Type Buffer::type("GraphicsBuffer", &Object::type);
|
||||
|
||||
Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDeclaration> &bufferformat, size_t size, size_t arraylength)
|
||||
: arrayLength(0)
|
||||
, arrayStride(0)
|
||||
, size(size)
|
||||
, typeFlags(settings.typeFlags)
|
||||
, usage(settings.usage)
|
||||
, mapFlags(settings.mapFlags)
|
||||
, mapped(false)
|
||||
{
|
||||
if (size == 0 && arraylength == 0)
|
||||
throw love::Exception("Size or array length must be specified.");
|
||||
|
||||
if (bufferformat.size() == 0)
|
||||
throw love::Exception("Data format must contain values.");
|
||||
|
||||
const auto &caps = gfx->getCapabilities();
|
||||
bool supportsGLSL3 = caps.features[Graphics::FEATURE_GLSL3];
|
||||
|
||||
bool indexbuffer = settings.typeFlags & TYPEFLAG_INDEX;
|
||||
bool vertexbuffer = settings.typeFlags & TYPEFLAG_VERTEX;
|
||||
bool texelbuffer = settings.typeFlags & TYPEFLAG_TEXEL;
|
||||
|
||||
if (!indexbuffer && !vertexbuffer && !texelbuffer)
|
||||
throw love::Exception("Buffer must be created with at least one buffer type (index, vertex, or texel).");
|
||||
|
||||
if (texelbuffer && !caps.features[Graphics::FEATURE_TEXEL_BUFFER])
|
||||
throw love::Exception("Texel buffers are not supported on this system.");
|
||||
|
||||
size_t offset = 0;
|
||||
size_t stride = 0;
|
||||
|
||||
for (const DataDeclaration &decl : bufferformat)
|
||||
{
|
||||
DataMember member(decl);
|
||||
|
||||
DataFormat format = member.decl.format;
|
||||
const DataFormatInfo &info = member.info;
|
||||
|
||||
if (indexbuffer)
|
||||
{
|
||||
if (format != DATAFORMAT_UINT16 && format != DATAFORMAT_UINT32)
|
||||
throw love::Exception("Index buffers only support uint16 and uint32 data types.");
|
||||
|
||||
if (bufferformat.size() > 1)
|
||||
throw love::Exception("Index buffers only support a single value per element.");
|
||||
|
||||
if (decl.arrayLength > 0)
|
||||
throw love::Exception("Arrays are not supported in index buffers.");
|
||||
}
|
||||
|
||||
if (vertexbuffer)
|
||||
{
|
||||
if (decl.arrayLength > 0)
|
||||
throw love::Exception("Arrays are not supported in vertex buffers.");
|
||||
|
||||
if (info.isMatrix)
|
||||
throw love::Exception("Matrix types are not supported in vertex buffers.");
|
||||
|
||||
if (info.baseType == DATA_BASETYPE_BOOL)
|
||||
throw love::Exception("Bool types are not supported in vertex buffers.");
|
||||
|
||||
if ((info.baseType == DATA_BASETYPE_INT || info.baseType == DATA_BASETYPE_UINT) && !supportsGLSL3)
|
||||
throw love::Exception("Integer vertex attribute data types require GLSL 3 support.");
|
||||
|
||||
if (decl.name.empty())
|
||||
throw love::Exception("Vertex buffer attributes must have a name.");
|
||||
}
|
||||
|
||||
if (texelbuffer)
|
||||
{
|
||||
if (format != bufferformat[0].format)
|
||||
throw love::Exception("All values in a texel buffer must have the same format.");
|
||||
|
||||
if (decl.arrayLength > 0)
|
||||
throw love::Exception("Arrays are not supported in texel buffers.");
|
||||
|
||||
if (info.isMatrix)
|
||||
throw love::Exception("Matrix types are not supported in texel buffers.");
|
||||
|
||||
if (info.baseType == DATA_BASETYPE_BOOL)
|
||||
throw love::Exception("Bool types are not supported in texel buffers.");
|
||||
|
||||
if (info.components == 3)
|
||||
throw love::Exception("3-component formats are not supported in texel buffers.");
|
||||
|
||||
if (info.baseType == DATA_BASETYPE_SNORM)
|
||||
throw love::Exception("Signed normalized formats are not supported in texel buffers.");
|
||||
}
|
||||
|
||||
// TODO: alignment
|
||||
member.offset = offset;
|
||||
member.size = member.info.size;
|
||||
|
||||
offset += member.size;
|
||||
|
||||
dataMembers.push_back(member);
|
||||
}
|
||||
|
||||
stride = offset;
|
||||
|
||||
if (size != 0)
|
||||
{
|
||||
size_t remainder = size % stride;
|
||||
if (remainder > 0)
|
||||
size += stride - remainder;
|
||||
arraylength = size / stride;
|
||||
}
|
||||
else
|
||||
{
|
||||
size = arraylength * stride;
|
||||
}
|
||||
|
||||
this->arrayStride = stride;
|
||||
this->arrayLength = arraylength;
|
||||
this->size = size;
|
||||
|
||||
if (texelbuffer && arraylength * dataMembers.size() > caps.limits[Graphics::LIMIT_TEXEL_BUFFER_SIZE])
|
||||
throw love::Exception("Cannot create texel buffer: total number of values in the buffer (%d * %d) is too large for this system (maximum %d).", (int) dataMembers.size(), (int) arraylength, caps.limits[Graphics::LIMIT_TEXEL_BUFFER_SIZE]);
|
||||
}
|
||||
|
||||
Buffer::~Buffer()
|
||||
{
|
||||
}
|
||||
|
||||
int Buffer::getDataMemberIndex(const std::string &name) const
|
||||
{
|
||||
for (size_t i = 0; i < dataMembers.size(); i++)
|
||||
{
|
||||
if (dataMembers[i].decl.name == name)
|
||||
return (int) i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::vector<Buffer::DataDeclaration> Buffer::getCommonFormatDeclaration(CommonFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case CommonFormat::NONE:
|
||||
return {};
|
||||
case CommonFormat::XYf:
|
||||
return {
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 }
|
||||
};
|
||||
case CommonFormat::XYZf:
|
||||
return {
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC3 }
|
||||
};
|
||||
case CommonFormat::RGBAub:
|
||||
return {
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 }
|
||||
};
|
||||
case CommonFormat::STf_RGBAub:
|
||||
return {
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2 },
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 },
|
||||
};
|
||||
case CommonFormat::STPf_RGBAub:
|
||||
return {
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC3 },
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 },
|
||||
};
|
||||
case CommonFormat::XYf_STf:
|
||||
return {
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 },
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2 },
|
||||
};
|
||||
case CommonFormat::XYf_STPf:
|
||||
return {
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 },
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC3 },
|
||||
};
|
||||
case CommonFormat::XYf_STf_RGBAub:
|
||||
return {
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 },
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2 },
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 },
|
||||
};
|
||||
case CommonFormat::XYf_STus_RGBAub:
|
||||
return {
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 },
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_UNORM16_VEC2 },
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 },
|
||||
};
|
||||
case CommonFormat::XYf_STPf_RGBAub:
|
||||
return {
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 },
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2 },
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 },
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -23,40 +23,102 @@
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "common/int.h"
|
||||
#include "common/Object.h"
|
||||
#include "vertex.h"
|
||||
#include "Resource.h"
|
||||
|
||||
// C
|
||||
#include <stddef.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
class Graphics;
|
||||
|
||||
/**
|
||||
* A block of GPU-owned memory. Currently meant for internal use.
|
||||
* A block of GPU-owned memory.
|
||||
**/
|
||||
class Buffer : public Resource
|
||||
class Buffer : public love::Object, public Resource
|
||||
{
|
||||
public:
|
||||
|
||||
static love::Type type;
|
||||
|
||||
enum MapFlags
|
||||
{
|
||||
MAP_NONE = 0,
|
||||
MAP_EXPLICIT_RANGE_MODIFY = (1 << 0), // see setMappedRangeModified.
|
||||
MAP_READ = (1 << 1),
|
||||
};
|
||||
|
||||
Buffer(size_t size, BufferType type, vertex::Usage usage, uint32 mapflags);
|
||||
enum TypeFlags
|
||||
{
|
||||
TYPEFLAG_NONE = 0,
|
||||
TYPEFLAG_VERTEX = 1 << BUFFERTYPE_VERTEX,
|
||||
TYPEFLAG_INDEX = 1 << BUFFERTYPE_INDEX,
|
||||
TYPEFLAG_TEXEL = 1 << BUFFERTYPE_TEXEL,
|
||||
};
|
||||
|
||||
struct DataDeclaration
|
||||
{
|
||||
std::string name;
|
||||
DataFormat format;
|
||||
int arrayLength;
|
||||
|
||||
DataDeclaration(const std::string &name, DataFormat format, int arrayLength = 0)
|
||||
: name(name)
|
||||
, format(format)
|
||||
, arrayLength(arrayLength)
|
||||
{}
|
||||
};
|
||||
|
||||
struct DataMember
|
||||
{
|
||||
DataDeclaration decl;
|
||||
DataFormatInfo info;
|
||||
size_t offset;
|
||||
size_t size;
|
||||
|
||||
DataMember(const DataDeclaration &decl)
|
||||
: decl(decl)
|
||||
, info(getDataFormatInfo(decl.format))
|
||||
, offset(0)
|
||||
, size(0)
|
||||
{}
|
||||
};
|
||||
|
||||
struct Settings
|
||||
{
|
||||
TypeFlags typeFlags;
|
||||
MapFlags mapFlags;
|
||||
BufferUsage usage;
|
||||
|
||||
Settings(uint32 typeflags, uint32 mapflags, BufferUsage usage)
|
||||
: typeFlags((TypeFlags)typeflags)
|
||||
, mapFlags((MapFlags)mapflags)
|
||||
, usage(usage)
|
||||
{}
|
||||
};
|
||||
|
||||
Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDeclaration> &format, size_t size, size_t arraylength);
|
||||
virtual ~Buffer();
|
||||
|
||||
size_t getSize() const { return size; }
|
||||
TypeFlags getTypeFlags() const { return typeFlags; }
|
||||
BufferUsage getUsage() const { return usage; }
|
||||
bool isMapped() const { return mapped; }
|
||||
uint32 getMapFlags() const { return mapFlags; }
|
||||
|
||||
BufferType getType() const { return type; }
|
||||
|
||||
vertex::Usage getUsage() const { return usage; }
|
||||
|
||||
bool isMapped() const { return is_mapped; }
|
||||
size_t getArrayLength() const { return arrayLength; }
|
||||
size_t getArrayStride() const { return arrayStride; }
|
||||
const std::vector<DataMember> &getDataMembers() const { return dataMembers; }
|
||||
const DataMember &getDataMember(int index) const { return dataMembers[index]; }
|
||||
size_t getMemberOffset(int index) const { return dataMembers[index].offset; }
|
||||
int getDataMemberIndex(const std::string &name) const;
|
||||
|
||||
/**
|
||||
* Map the Buffer to client memory.
|
||||
@@ -80,10 +142,6 @@ public:
|
||||
|
||||
/**
|
||||
* Fill a portion of the buffer with data and marks the range as modified.
|
||||
*
|
||||
* @param offset The offset in the GLBuffer to store the data.
|
||||
* @param size The size of the incoming data.
|
||||
* @param data Pointer to memory to copy data from.
|
||||
*/
|
||||
virtual void fill(size_t offset, size_t size, const void *data) = 0;
|
||||
|
||||
@@ -92,58 +150,54 @@ public:
|
||||
**/
|
||||
virtual void copyTo(size_t offset, size_t size, Buffer *other, size_t otheroffset) = 0;
|
||||
|
||||
uint32 getMapFlags() const { return map_flags; }
|
||||
/**
|
||||
* Texel buffers may use an additional texture handle as well as a buffer
|
||||
* handle.
|
||||
**/
|
||||
virtual ptrdiff_t getTexelBufferHandle() const = 0;
|
||||
|
||||
static std::vector<DataDeclaration> getCommonFormatDeclaration(CommonFormat format);
|
||||
|
||||
class Mapper
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Memory-maps a Buffer.
|
||||
*/
|
||||
Mapper(Buffer &buffer)
|
||||
: buf(buffer)
|
||||
: buffer(buffer)
|
||||
{
|
||||
elems = buf.map();
|
||||
data = buffer.map();
|
||||
}
|
||||
|
||||
/**
|
||||
* unmaps the buffer
|
||||
*/
|
||||
~Mapper()
|
||||
{
|
||||
buf.unmap();
|
||||
if (buffer.getMapFlags() & MAP_EXPLICIT_RANGE_MODIFY)
|
||||
buffer.setMappedRangeModified(0, buffer.getSize());
|
||||
buffer.unmap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pointer to memory mapped region
|
||||
*/
|
||||
void *get()
|
||||
{
|
||||
return elems;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Buffer &buf;
|
||||
void *elems;
|
||||
Buffer &buffer;
|
||||
void *data;
|
||||
|
||||
}; // Mapper
|
||||
|
||||
protected:
|
||||
|
||||
std::vector<DataMember> dataMembers;
|
||||
size_t arrayLength;
|
||||
size_t arrayStride;
|
||||
|
||||
// The size of the buffer, in bytes.
|
||||
size_t size;
|
||||
|
||||
// The type of the buffer object.
|
||||
BufferType type;
|
||||
TypeFlags typeFlags;
|
||||
|
||||
// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
|
||||
vertex::Usage usage;
|
||||
BufferUsage usage;
|
||||
|
||||
uint32 map_flags;
|
||||
uint32 mapFlags;
|
||||
|
||||
bool is_mapped;
|
||||
bool mapped;
|
||||
|
||||
}; // Buffer
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ static inline uint16 normToUint16(double n)
|
||||
love::Type Font::type("Font", &Object::type);
|
||||
int Font::fontCount = 0;
|
||||
|
||||
const vertex::CommonFormat Font::vertexFormat = vertex::CommonFormat::XYf_STus_RGBAub;
|
||||
const CommonFormat Font::vertexFormat = CommonFormat::XYf_STus_RGBAub;
|
||||
|
||||
Font::Font(love::font::Rasterizer *r, const SamplerState &s)
|
||||
: rasterizers({r})
|
||||
@@ -647,7 +647,7 @@ void Font::printv(graphics::Graphics *gfx, const Matrix4 &t, const std::vector<D
|
||||
{
|
||||
Graphics::BatchedDrawCommand streamcmd;
|
||||
streamcmd.formats[0] = vertexFormat;
|
||||
streamcmd.indexMode = vertex::TriangleIndexMode::QUADS;
|
||||
streamcmd.indexMode = TRIANGLEINDEX_QUADS;
|
||||
streamcmd.vertexCount = cmd.vertexcount;
|
||||
streamcmd.texture = cmd.texture;
|
||||
|
||||
|
||||
@@ -51,9 +51,9 @@ public:
|
||||
static love::Type type;
|
||||
|
||||
typedef std::vector<uint32> Codepoints;
|
||||
typedef vertex::XYf_STus_RGBAub GlyphVertex;
|
||||
typedef XYf_STus_RGBAub GlyphVertex;
|
||||
|
||||
static const vertex::CommonFormat vertexFormat;
|
||||
static const CommonFormat vertexFormat;
|
||||
|
||||
enum AlignMode
|
||||
{
|
||||
|
||||
@@ -105,8 +105,6 @@ bool isDebugEnabled()
|
||||
|
||||
love::Type Graphics::type("graphics", &Module::type);
|
||||
|
||||
Graphics::DefaultShaderCode Graphics::defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2];
|
||||
|
||||
namespace opengl { extern love::graphics::Graphics *createInstance(); }
|
||||
#if defined(LOVE_MACOS) || defined(LOVE_IOS)
|
||||
namespace metal { extern love::graphics::Graphics *createInstance(); }
|
||||
@@ -203,10 +201,12 @@ void Graphics::createQuadIndexBuffer()
|
||||
return;
|
||||
|
||||
size_t size = sizeof(uint16) * (LOVE_UINT16_MAX / 4) * 6;
|
||||
quadIndexBuffer = newBuffer(size, nullptr, BUFFER_INDEX, vertex::USAGE_STATIC, 0);
|
||||
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, 0, BUFFERUSAGE_STATIC);
|
||||
quadIndexBuffer = newBuffer(settings, DATAFORMAT_UINT16, nullptr, size, 0);
|
||||
|
||||
Buffer::Mapper map(*quadIndexBuffer);
|
||||
vertex::fillIndices(vertex::TriangleIndexMode::QUADS, 0, LOVE_UINT16_MAX, (uint16 *) map.get());
|
||||
fillIndices(TRIANGLEINDEX_QUADS, 0, LOVE_UINT16_MAX, (uint16 *) map.data);
|
||||
}
|
||||
|
||||
Quad *Graphics::newQuad(Quad::Viewport v, double sw, double sh)
|
||||
@@ -234,7 +234,7 @@ Video *Graphics::newVideo(love::video::VideoStream *stream, float dpiscale)
|
||||
return new Video(this, stream, dpiscale);
|
||||
}
|
||||
|
||||
love::graphics::SpriteBatch *Graphics::newSpriteBatch(Texture *texture, int size, vertex::Usage usage)
|
||||
love::graphics::SpriteBatch *Graphics::newSpriteBatch(Texture *texture, int size, BufferUsage usage)
|
||||
{
|
||||
return new SpriteBatch(this, texture, size, usage);
|
||||
}
|
||||
@@ -244,13 +244,8 @@ love::graphics::ParticleSystem *Graphics::newParticleSystem(Texture *texture, in
|
||||
return new ParticleSystem(texture, size);
|
||||
}
|
||||
|
||||
ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::string &optsource)
|
||||
ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::string &source, const Shader::SourceInfo &info)
|
||||
{
|
||||
if (stage == ShaderStage::STAGE_MAX_ENUM)
|
||||
throw love::Exception("Invalid shader stage.");
|
||||
|
||||
const std::string &source = optsource.empty() ? getCurrentDefaultShaderCode().source[stage] : optsource;
|
||||
|
||||
ShaderStage *s = nullptr;
|
||||
std::string cachekey;
|
||||
|
||||
@@ -271,7 +266,8 @@ ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::s
|
||||
|
||||
if (s == nullptr)
|
||||
{
|
||||
s = newShaderStageInternal(stage, cachekey, source, usesGLSLES());
|
||||
std::string glsl = Shader::createShaderStageCode(this, stage, source, info);
|
||||
s = newShaderStageInternal(stage, cachekey, glsl, usesGLSLES());
|
||||
if (!cachekey.empty())
|
||||
cachedShaderStages[stage][cachekey] = s;
|
||||
}
|
||||
@@ -279,37 +275,71 @@ ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::s
|
||||
return s;
|
||||
}
|
||||
|
||||
Shader *Graphics::newShader(const std::string &vertex, const std::string &pixel)
|
||||
Shader *Graphics::newShader(const std::vector<std::string> &stagessource)
|
||||
{
|
||||
if (vertex.empty() && pixel.empty())
|
||||
throw love::Exception("Error creating shader: no source code!");
|
||||
StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM] = {};
|
||||
|
||||
StrongRef<ShaderStage> vertexstage(newShaderStage(ShaderStage::STAGE_VERTEX, vertex), Acquire::NORETAIN);
|
||||
StrongRef<ShaderStage> pixelstage(newShaderStage(ShaderStage::STAGE_PIXEL, pixel), Acquire::NORETAIN);
|
||||
bool validstages[ShaderStage::STAGE_MAX_ENUM] = {};
|
||||
validstages[ShaderStage::STAGE_VERTEX] = true;
|
||||
validstages[ShaderStage::STAGE_PIXEL] = true;
|
||||
|
||||
return newShaderInternal(vertexstage.get(), pixelstage.get());
|
||||
for (const std::string &source : stagessource)
|
||||
{
|
||||
Shader::SourceInfo info = Shader::getSourceInfo(source);
|
||||
bool isanystage = false;
|
||||
|
||||
for (int i = 0; i < ShaderStage::STAGE_MAX_ENUM; i++)
|
||||
{
|
||||
if (!validstages[i])
|
||||
continue;
|
||||
|
||||
if (info.isStage[i])
|
||||
{
|
||||
isanystage = true;
|
||||
stages[i].set(newShaderStage((ShaderStage::StageType) i, source, info), Acquire::NORETAIN);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isanystage)
|
||||
throw love::Exception("Could not parse shader code (missing 'position' or 'effect' function?)");
|
||||
}
|
||||
|
||||
for (int i = 0; i < ShaderStage::STAGE_MAX_ENUM; i++)
|
||||
{
|
||||
auto stype = (ShaderStage::StageType) i;
|
||||
if (validstages[i] && stages[i].get() == nullptr)
|
||||
{
|
||||
const std::string &source = Shader::getDefaultCode(Shader::STANDARD_DEFAULT, stype);
|
||||
Shader::SourceInfo info = Shader::getSourceInfo(source);
|
||||
stages[i].set(newShaderStage(stype, source, info), Acquire::NORETAIN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return newShaderInternal(stages[ShaderStage::STAGE_VERTEX], stages[ShaderStage::STAGE_PIXEL]);
|
||||
}
|
||||
|
||||
Mesh *Graphics::newMesh(const std::vector<Vertex> &vertices, PrimitiveType drawmode, vertex::Usage usage)
|
||||
Buffer *Graphics::newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength)
|
||||
{
|
||||
return newMesh(Mesh::getDefaultVertexFormat(), &vertices[0], vertices.size() * sizeof(Vertex), drawmode, usage);
|
||||
std::vector<Buffer::DataDeclaration> dataformat = {{"", format, 0}};
|
||||
return newBuffer(settings, dataformat, data, size, arraylength);
|
||||
}
|
||||
|
||||
Mesh *Graphics::newMesh(int vertexcount, PrimitiveType drawmode, vertex::Usage usage)
|
||||
{
|
||||
return newMesh(Mesh::getDefaultVertexFormat(), vertexcount, drawmode, usage);
|
||||
}
|
||||
|
||||
love::graphics::Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, PrimitiveType drawmode, vertex::Usage usage)
|
||||
Mesh *Graphics::newMesh(const std::vector<Buffer::DataDeclaration> &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage)
|
||||
{
|
||||
return new Mesh(this, vertexformat, vertexcount, drawmode, usage);
|
||||
}
|
||||
|
||||
love::graphics::Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, vertex::Usage usage)
|
||||
Mesh *Graphics::newMesh(const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage)
|
||||
{
|
||||
return new Mesh(this, vertexformat, data, datasize, drawmode, usage);
|
||||
}
|
||||
|
||||
Mesh *Graphics::newMesh(const std::vector<Mesh::BufferAttribute> &attributes, PrimitiveType drawmode)
|
||||
{
|
||||
return new Mesh(attributes, drawmode);
|
||||
}
|
||||
|
||||
love::graphics::Text *Graphics::newText(graphics::Font *font, const std::vector<Font::ColoredString> &text)
|
||||
{
|
||||
return new Text(font, text);
|
||||
@@ -320,26 +350,44 @@ void Graphics::cleanupCachedShaderStage(ShaderStage::StageType type, const std::
|
||||
cachedShaderStages[type].erase(hashkey);
|
||||
}
|
||||
|
||||
bool Graphics::validateShader(bool gles, const std::string &vertex, const std::string &pixel, std::string &err)
|
||||
bool Graphics::validateShader(bool gles, const std::vector<std::string> &stagessource, std::string &err)
|
||||
{
|
||||
if (vertex.empty() && pixel.empty())
|
||||
{
|
||||
err = "Error validating shader: no source code!";
|
||||
return false;
|
||||
}
|
||||
StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM] = {};
|
||||
|
||||
StrongRef<ShaderStage> vertexstage;
|
||||
StrongRef<ShaderStage> pixelstage;
|
||||
bool validstages[ShaderStage::STAGE_MAX_ENUM] = {};
|
||||
validstages[ShaderStage::STAGE_VERTEX] = true;
|
||||
validstages[ShaderStage::STAGE_PIXEL] = true;
|
||||
|
||||
// Don't use cached shader stages, since the gles flag may not match the
|
||||
// current renderer.
|
||||
if (!vertex.empty())
|
||||
vertexstage.set(new ShaderStageForValidation(this, ShaderStage::STAGE_VERTEX, vertex, gles), Acquire::NORETAIN);
|
||||
for (const std::string &source : stagessource)
|
||||
{
|
||||
Shader::SourceInfo info = Shader::getSourceInfo(source);
|
||||
bool isanystage = false;
|
||||
|
||||
if (!pixel.empty())
|
||||
pixelstage.set(new ShaderStageForValidation(this, ShaderStage::STAGE_PIXEL, pixel, gles), Acquire::NORETAIN);
|
||||
for (int i = 0; i < ShaderStage::STAGE_MAX_ENUM; i++)
|
||||
{
|
||||
auto stype = (ShaderStage::StageType) i;
|
||||
|
||||
return Shader::validate(vertexstage.get(), pixelstage.get(), err);
|
||||
if (!validstages[i])
|
||||
continue;
|
||||
|
||||
if (info.isStage[i])
|
||||
{
|
||||
isanystage = true;
|
||||
std::string glsl = Shader::createShaderStageCode(this, stype, source, info);
|
||||
stages[i].set(new ShaderStageForValidation(this, stype, glsl, gles), Acquire::NORETAIN);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isanystage)
|
||||
{
|
||||
err = "Could not parse shader code (missing 'position' or 'effect' function?)";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return Shader::validate(stages[ShaderStage::STAGE_VERTEX], stages[ShaderStage::STAGE_PIXEL], err);
|
||||
}
|
||||
|
||||
int Graphics::getWidth() const
|
||||
@@ -942,7 +990,7 @@ CullMode Graphics::getMeshCullMode() const
|
||||
return states.back().meshCullMode;
|
||||
}
|
||||
|
||||
vertex::Winding Graphics::getFrontFaceWinding() const
|
||||
Winding Graphics::getFrontFaceWinding() const
|
||||
{
|
||||
return states.back().winding;
|
||||
}
|
||||
@@ -1031,8 +1079,6 @@ void Graphics::captureScreenshot(const ScreenshotInfo &info)
|
||||
|
||||
Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawCommand &cmd)
|
||||
{
|
||||
using namespace vertex;
|
||||
|
||||
BatchedDrawState &state = batchedDrawState;
|
||||
|
||||
bool shouldflush = false;
|
||||
@@ -1040,7 +1086,7 @@ Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawComman
|
||||
|
||||
if (cmd.primitiveMode != state.primitiveMode
|
||||
|| cmd.formats[0] != state.formats[0] || cmd.formats[1] != state.formats[1]
|
||||
|| ((cmd.indexMode != TriangleIndexMode::NONE) != (state.indexCount > 0))
|
||||
|| ((cmd.indexMode != TRIANGLEINDEX_NONE) != (state.indexCount > 0))
|
||||
|| cmd.texture != state.texture
|
||||
|| cmd.standardShaderType != state.standardShaderType)
|
||||
{
|
||||
@@ -1050,7 +1096,7 @@ Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawComman
|
||||
int totalvertices = state.vertexCount + cmd.vertexCount;
|
||||
|
||||
// We only support uint16 index buffers for now.
|
||||
if (totalvertices > LOVE_UINT16_MAX && cmd.indexMode != TriangleIndexMode::NONE)
|
||||
if (totalvertices > LOVE_UINT16_MAX && cmd.indexMode != TRIANGLEINDEX_NONE)
|
||||
shouldflush = true;
|
||||
|
||||
int reqIndexCount = getIndexCount(cmd.indexMode, cmd.vertexCount);
|
||||
@@ -1079,7 +1125,7 @@ Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawComman
|
||||
newdatasizes[i] = stride * cmd.vertexCount;
|
||||
}
|
||||
|
||||
if (cmd.indexMode != TriangleIndexMode::NONE)
|
||||
if (cmd.indexMode != TRIANGLEINDEX_NONE)
|
||||
{
|
||||
size_t datasize = (state.indexCount + reqIndexCount) * sizeof(uint16);
|
||||
|
||||
@@ -1117,18 +1163,18 @@ Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawComman
|
||||
if (state.vb[i]->getSize() < buffersizes[i])
|
||||
{
|
||||
delete state.vb[i];
|
||||
state.vb[i] = newStreamBuffer(BUFFER_VERTEX, buffersizes[i]);
|
||||
state.vb[i] = newStreamBuffer(BUFFERTYPE_VERTEX, buffersizes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.indexBuffer->getSize() < buffersizes[2])
|
||||
{
|
||||
delete state.indexBuffer;
|
||||
state.indexBuffer = newStreamBuffer(BUFFER_INDEX, buffersizes[2]);
|
||||
state.indexBuffer = newStreamBuffer(BUFFERTYPE_INDEX, buffersizes[2]);
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd.indexMode != TriangleIndexMode::NONE)
|
||||
if (cmd.indexMode != TRIANGLEINDEX_NONE)
|
||||
{
|
||||
if (state.indexBufferMap.data == nullptr)
|
||||
state.indexBufferMap = state.indexBuffer->map(reqIndexSize);
|
||||
@@ -1165,14 +1211,12 @@ Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawComman
|
||||
|
||||
void Graphics::flushBatchedDraws()
|
||||
{
|
||||
using namespace vertex;
|
||||
|
||||
auto &sbstate = batchedDrawState;
|
||||
|
||||
if (sbstate.vertexCount == 0 && sbstate.indexCount == 0)
|
||||
return;
|
||||
|
||||
Attributes attributes;
|
||||
VertexAttributes attributes;
|
||||
BufferBindings buffers;
|
||||
|
||||
size_t usedsizes[3] = {0, 0, 0};
|
||||
@@ -1315,8 +1359,8 @@ void Graphics::points(const Vector2 *positions, const Colorf *colors, size_t num
|
||||
|
||||
BatchedDrawCommand cmd;
|
||||
cmd.primitiveMode = PRIMITIVE_POINTS;
|
||||
cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
|
||||
cmd.formats[1] = vertex::CommonFormat::RGBAub;
|
||||
cmd.formats[0] = getSinglePositionFormat(is2D);
|
||||
cmd.formats[1] = CommonFormat::RGBAub;
|
||||
cmd.vertexCount = (int) numpoints;
|
||||
|
||||
BatchedVertexData data = requestBatchedDraw(cmd);
|
||||
@@ -1610,9 +1654,9 @@ void Graphics::polygon(DrawMode mode, const Vector2 *coords, size_t count, bool
|
||||
bool is2D = t.isAffine2DTransform();
|
||||
|
||||
BatchedDrawCommand cmd;
|
||||
cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
|
||||
cmd.formats[1] = vertex::CommonFormat::RGBAub;
|
||||
cmd.indexMode = vertex::TriangleIndexMode::FAN;
|
||||
cmd.formats[0] = getSinglePositionFormat(is2D);
|
||||
cmd.formats[1] = CommonFormat::RGBAub;
|
||||
cmd.indexMode = TRIANGLEINDEX_FAN;
|
||||
cmd.vertexCount = (int)count - (skipLastFilledVertex ? 1 : 0);
|
||||
|
||||
BatchedVertexData data = requestBatchedDraw(cmd);
|
||||
@@ -1786,14 +1830,6 @@ Vector2 Graphics::inverseTransformPoint(Vector2 point)
|
||||
return p;
|
||||
}
|
||||
|
||||
const Graphics::DefaultShaderCode &Graphics::getCurrentDefaultShaderCode() const
|
||||
{
|
||||
int languageindex = (int) getShaderLanguageTarget();
|
||||
int gammaindex = isGammaCorrect() ? 1 : 0;
|
||||
|
||||
return defaultShaderCode[Shader::STANDARD_DEFAULT][languageindex][gammaindex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants.
|
||||
**/
|
||||
@@ -1939,6 +1975,7 @@ StringMap<Graphics::Feature, Graphics::FEATURE_MAX_ENUM>::Entry Graphics::featur
|
||||
{ "glsl3", FEATURE_GLSL3 },
|
||||
{ "glsl4", FEATURE_GLSL4 },
|
||||
{ "instancing", FEATURE_INSTANCING },
|
||||
{ "texelbuffer", FEATURE_TEXEL_BUFFER },
|
||||
};
|
||||
|
||||
StringMap<Graphics::Feature, Graphics::FEATURE_MAX_ENUM> Graphics::features(Graphics::featureEntries, sizeof(Graphics::featureEntries));
|
||||
@@ -1950,6 +1987,7 @@ StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM>::Entry Graphics::syst
|
||||
{ "texturelayers", LIMIT_TEXTURE_LAYERS },
|
||||
{ "volumetexturesize", LIMIT_VOLUME_TEXTURE_SIZE },
|
||||
{ "cubetexturesize", LIMIT_CUBE_TEXTURE_SIZE },
|
||||
{ "texelbuffersize", LIMIT_TEXEL_BUFFER_SIZE },
|
||||
{ "rendertargets", LIMIT_RENDER_TARGETS },
|
||||
{ "texturemsaa", LIMIT_TEXTURE_MSAA },
|
||||
{ "anisotropy", LIMIT_ANISOTROPY },
|
||||
|
||||
@@ -143,6 +143,7 @@ public:
|
||||
FEATURE_GLSL3,
|
||||
FEATURE_GLSL4,
|
||||
FEATURE_INSTANCING,
|
||||
FEATURE_TEXEL_BUFFER,
|
||||
FEATURE_MAX_ENUM
|
||||
};
|
||||
|
||||
@@ -161,6 +162,7 @@ public:
|
||||
LIMIT_VOLUME_TEXTURE_SIZE,
|
||||
LIMIT_CUBE_TEXTURE_SIZE,
|
||||
LIMIT_TEXTURE_LAYERS,
|
||||
LIMIT_TEXEL_BUFFER_SIZE,
|
||||
LIMIT_RENDER_TARGETS,
|
||||
LIMIT_TEXTURE_MSAA,
|
||||
LIMIT_ANISOTROPY,
|
||||
@@ -210,8 +212,8 @@ public:
|
||||
{
|
||||
PrimitiveType primitiveType = PRIMITIVE_TRIANGLES;
|
||||
|
||||
const vertex::Attributes *attributes;
|
||||
const vertex::BufferBindings *buffers;
|
||||
const VertexAttributes *attributes;
|
||||
const BufferBindings *buffers;
|
||||
|
||||
int vertexStart = 0;
|
||||
int vertexCount = 0;
|
||||
@@ -222,7 +224,7 @@ public:
|
||||
// TODO: This should be moved out to a state transition API?
|
||||
CullMode cullMode = CULL_NONE;
|
||||
|
||||
DrawCommand(const vertex::Attributes *attribs, const vertex::BufferBindings *buffers)
|
||||
DrawCommand(const VertexAttributes *attribs, const BufferBindings *buffers)
|
||||
: attributes(attribs)
|
||||
, buffers(buffers)
|
||||
{}
|
||||
@@ -232,8 +234,8 @@ public:
|
||||
{
|
||||
PrimitiveType primitiveType = PRIMITIVE_TRIANGLES;
|
||||
|
||||
const vertex::Attributes *attributes;
|
||||
const vertex::BufferBindings *buffers;
|
||||
const VertexAttributes *attributes;
|
||||
const BufferBindings *buffers;
|
||||
|
||||
int indexCount = 0;
|
||||
int instanceCount = 1;
|
||||
@@ -247,7 +249,7 @@ public:
|
||||
// TODO: This should be moved out to a state transition API?
|
||||
CullMode cullMode = CULL_NONE;
|
||||
|
||||
DrawIndexedCommand(const vertex::Attributes *attribs, const vertex::BufferBindings *buffers, Resource *indexbuffer)
|
||||
DrawIndexedCommand(const VertexAttributes *attribs, const BufferBindings *buffers, Resource *indexbuffer)
|
||||
: attributes(attribs)
|
||||
, buffers(buffers)
|
||||
, indexBuffer(indexbuffer)
|
||||
@@ -257,8 +259,8 @@ public:
|
||||
struct BatchedDrawCommand
|
||||
{
|
||||
PrimitiveType primitiveMode = PRIMITIVE_TRIANGLES;
|
||||
vertex::CommonFormat formats[2];
|
||||
vertex::TriangleIndexMode indexMode = vertex::TriangleIndexMode::NONE;
|
||||
CommonFormat formats[2];
|
||||
TriangleIndexMode indexMode = TRIANGLEINDEX_NONE;
|
||||
int vertexCount = 0;
|
||||
Texture *texture = nullptr;
|
||||
Shader::StandardShader standardShaderType = Shader::STANDARD_DEFAULT;
|
||||
@@ -266,7 +268,7 @@ public:
|
||||
BatchedDrawCommand()
|
||||
{
|
||||
// VS2013 can't initialize arrays in the above manner...
|
||||
formats[1] = formats[0] = vertex::CommonFormat::NONE;
|
||||
formats[1] = formats[0] = CommonFormat::NONE;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -416,11 +418,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct DefaultShaderCode
|
||||
{
|
||||
std::string source[ShaderStage::STAGE_MAX_ENUM];
|
||||
};
|
||||
|
||||
Graphics();
|
||||
virtual ~Graphics();
|
||||
|
||||
@@ -434,22 +431,21 @@ public:
|
||||
Font *newDefaultFont(int size, font::TrueTypeRasterizer::Hinting hinting);
|
||||
Video *newVideo(love::video::VideoStream *stream, float dpiscale);
|
||||
|
||||
SpriteBatch *newSpriteBatch(Texture *texture, int size, vertex::Usage usage);
|
||||
SpriteBatch *newSpriteBatch(Texture *texture, int size, BufferUsage usage);
|
||||
ParticleSystem *newParticleSystem(Texture *texture, int size);
|
||||
|
||||
ShaderStage *newShaderStage(ShaderStage::StageType stage, const std::string &source);
|
||||
Shader *newShader(const std::string &vertex, const std::string &pixel);
|
||||
Shader *newShader(const std::vector<std::string> &stagessource);
|
||||
|
||||
virtual Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) = 0;
|
||||
virtual Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) = 0;
|
||||
virtual Buffer *newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength);
|
||||
|
||||
Mesh *newMesh(const std::vector<Vertex> &vertices, PrimitiveType drawmode, vertex::Usage usage);
|
||||
Mesh *newMesh(int vertexcount, PrimitiveType drawmode, vertex::Usage usage);
|
||||
Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, PrimitiveType drawmode, vertex::Usage usage);
|
||||
Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, vertex::Usage usage);
|
||||
Mesh *newMesh(const std::vector<Buffer::DataDeclaration> &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage);
|
||||
Mesh *newMesh(const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage);
|
||||
Mesh *newMesh(const std::vector<Mesh::BufferAttribute> &attributes, PrimitiveType drawmode);
|
||||
|
||||
Text *newText(Font *font, const std::vector<Font::ColoredString> &text = {});
|
||||
|
||||
bool validateShader(bool gles, const std::string &vertex, const std::string &pixel, std::string &err);
|
||||
bool validateShader(bool gles, const std::vector<std::string> &stages, std::string &err);
|
||||
|
||||
/**
|
||||
* Resets the current color, background color, line style, and so forth.
|
||||
@@ -589,8 +585,8 @@ public:
|
||||
void setMeshCullMode(CullMode cull);
|
||||
CullMode getMeshCullMode() const;
|
||||
|
||||
virtual void setFrontFaceWinding(vertex::Winding winding) = 0;
|
||||
vertex::Winding getFrontFaceWinding() const;
|
||||
virtual void setFrontFaceWinding(Winding winding) = 0;
|
||||
Winding getFrontFaceWinding() const;
|
||||
|
||||
/**
|
||||
* Sets the enabled color components when rendering.
|
||||
@@ -826,16 +822,13 @@ public:
|
||||
|
||||
virtual void draw(const DrawCommand &cmd) = 0;
|
||||
virtual void draw(const DrawIndexedCommand &cmd) = 0;
|
||||
virtual void drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, Texture *texture) = 0;
|
||||
virtual void drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, Texture *texture) = 0;
|
||||
|
||||
void flushBatchedDraws();
|
||||
BatchedVertexData requestBatchedDraw(const BatchedDrawCommand &command);
|
||||
|
||||
static void flushBatchedDrawsGlobal();
|
||||
|
||||
virtual Shader::Language getShaderLanguageTarget() const = 0;
|
||||
const DefaultShaderCode &getCurrentDefaultShaderCode() const;
|
||||
|
||||
void cleanupCachedShaderStage(ShaderStage::StageType type, const std::string &cachekey);
|
||||
|
||||
template <typename T>
|
||||
@@ -877,9 +870,6 @@ public:
|
||||
static bool getConstant(StackType in, const char *&out);
|
||||
static std::vector<std::string> getConstants(StackType);
|
||||
|
||||
// Default shader code (a shader is always required internally.)
|
||||
static DefaultShaderCode defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2];
|
||||
|
||||
protected:
|
||||
|
||||
struct DisplayState
|
||||
@@ -904,7 +894,7 @@ protected:
|
||||
bool depthWrite = false;
|
||||
|
||||
CullMode meshCullMode = CULL_NONE;
|
||||
vertex::Winding winding = vertex::WINDING_CCW;
|
||||
Winding winding = WINDING_CCW;
|
||||
|
||||
StrongRef<Font> font;
|
||||
StrongRef<Shader> shader;
|
||||
@@ -924,7 +914,7 @@ protected:
|
||||
StreamBuffer *indexBuffer = nullptr;
|
||||
|
||||
PrimitiveType primitiveMode = PRIMITIVE_TRIANGLES;
|
||||
vertex::CommonFormat formats[2];
|
||||
CommonFormat formats[2];
|
||||
StrongRef<Texture> texture;
|
||||
Shader::StandardShader standardShaderType = Shader::STANDARD_DEFAULT;
|
||||
int vertexCount = 0;
|
||||
@@ -936,7 +926,7 @@ protected:
|
||||
BatchedDrawState()
|
||||
{
|
||||
vb[0] = vb[1] = nullptr;
|
||||
formats[0] = formats[1] = vertex::CommonFormat::NONE;
|
||||
formats[0] = formats[1] = CommonFormat::NONE;
|
||||
vbMap[0] = vbMap[1] = StreamBuffer::MapInfo();
|
||||
}
|
||||
};
|
||||
@@ -952,6 +942,7 @@ protected:
|
||||
{}
|
||||
};
|
||||
|
||||
ShaderStage *newShaderStage(ShaderStage::StageType stage, const std::string &source, const Shader::SourceInfo &info);
|
||||
virtual ShaderStage *newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles) = 0;
|
||||
virtual Shader *newShaderInternal(ShaderStage *vertex, ShaderStage *pixel) = 0;
|
||||
virtual StreamBuffer *newStreamBuffer(BufferType type, size_t size) = 0;
|
||||
|
||||
+188
-192
@@ -34,36 +34,22 @@ namespace love
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
static const char *getBuiltinAttribName(BuiltinVertexAttribute attribid)
|
||||
{
|
||||
const char *name = "";
|
||||
vertex::getConstant(attribid, name);
|
||||
return name;
|
||||
}
|
||||
|
||||
static_assert(offsetof(Vertex, x) == sizeof(float) * 0, "Incorrect position offset in Vertex struct");
|
||||
static_assert(offsetof(Vertex, s) == sizeof(float) * 2, "Incorrect texture coordinate offset in Vertex struct");
|
||||
static_assert(offsetof(Vertex, color.r) == sizeof(float) * 4, "Incorrect color offset in Vertex struct");
|
||||
|
||||
std::vector<Mesh::AttribFormat> Mesh::getDefaultVertexFormat()
|
||||
std::vector<Buffer::DataDeclaration> Mesh::getDefaultVertexFormat()
|
||||
{
|
||||
// Corresponds to the love::Vertex struct.
|
||||
std::vector<Mesh::AttribFormat> vertexformat = {
|
||||
{ getBuiltinAttribName(ATTRIB_POS), vertex::DATA_FLOAT, 2 },
|
||||
{ getBuiltinAttribName(ATTRIB_TEXCOORD), vertex::DATA_FLOAT, 2 },
|
||||
{ getBuiltinAttribName(ATTRIB_COLOR), vertex::DATA_UNORM8, 4 },
|
||||
};
|
||||
|
||||
return vertexformat;
|
||||
return Buffer::getCommonFormatDeclaration(CommonFormat::XYf_STf_RGBAub);
|
||||
}
|
||||
|
||||
love::Type Mesh::type("Mesh", &Drawable::type);
|
||||
|
||||
Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, vertex::Usage usage)
|
||||
: vertexFormat(vertexformat)
|
||||
, vertexBuffer(nullptr)
|
||||
Mesh::Mesh(graphics::Graphics *gfx, const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage)
|
||||
: vertexBuffer(nullptr)
|
||||
, vertexCount(0)
|
||||
, vertexStride(0)
|
||||
, vertexScratchBuffer(nullptr)
|
||||
, indexBuffer(nullptr)
|
||||
, useIndexBuffer(false)
|
||||
, indexCount(0)
|
||||
@@ -72,29 +58,29 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexforma
|
||||
, rangeStart(-1)
|
||||
, rangeCount(-1)
|
||||
{
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ, usage);
|
||||
vertexBuffer.set(gfx->newBuffer(settings, vertexformat, data, datasize, 0), Acquire::NORETAIN);
|
||||
|
||||
vertexCount = vertexBuffer->getArrayLength();
|
||||
vertexStride = vertexBuffer->getArrayStride();
|
||||
vertexFormat = vertexBuffer->getDataMembers();
|
||||
|
||||
setupAttachedAttributes();
|
||||
calculateAttributeSizes(gfx);
|
||||
|
||||
vertexCount = datasize / vertexStride;
|
||||
indexDataType = vertex::getIndexDataTypeFromMax(vertexCount);
|
||||
|
||||
if (vertexCount == 0)
|
||||
throw love::Exception("Data size is too small for specified vertex attribute formats.");
|
||||
|
||||
vertexBuffer = gfx->newBuffer(datasize, data, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
|
||||
indexDataType = getIndexDataTypeFromMax(vertexCount);
|
||||
|
||||
vertexScratchBuffer = new char[vertexStride];
|
||||
}
|
||||
|
||||
Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, PrimitiveType drawmode, vertex::Usage usage)
|
||||
: vertexFormat(vertexformat)
|
||||
, vertexBuffer(nullptr)
|
||||
Mesh::Mesh(graphics::Graphics *gfx, const std::vector<Buffer::DataDeclaration> &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage)
|
||||
: vertexBuffer(nullptr)
|
||||
, vertexCount((size_t) vertexcount)
|
||||
, vertexStride(0)
|
||||
, vertexScratchBuffer(nullptr)
|
||||
, indexBuffer(nullptr)
|
||||
, useIndexBuffer(false)
|
||||
, indexCount(0)
|
||||
, indexDataType(vertex::getIndexDataTypeFromMax(vertexcount))
|
||||
, indexDataType(getIndexDataTypeFromMax(vertexcount))
|
||||
, primitiveType(drawmode)
|
||||
, rangeStart(-1)
|
||||
, rangeCount(-1)
|
||||
@@ -102,83 +88,82 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexforma
|
||||
if (vertexcount <= 0)
|
||||
throw love::Exception("Invalid number of vertices (%d).", vertexcount);
|
||||
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ, usage);
|
||||
vertexBuffer.set(gfx->newBuffer(settings, vertexformat, nullptr, 0, vertexcount), Acquire::NORETAIN);
|
||||
|
||||
vertexStride = vertexBuffer->getArrayStride();
|
||||
vertexFormat = vertexBuffer->getDataMembers();
|
||||
|
||||
setupAttachedAttributes();
|
||||
calculateAttributeSizes(gfx);
|
||||
|
||||
size_t buffersize = vertexCount * vertexStride;
|
||||
|
||||
vertexBuffer = gfx->newBuffer(buffersize, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
|
||||
|
||||
// Initialize the buffer's contents to 0.
|
||||
memset(vertexBuffer->map(), 0, buffersize);
|
||||
memset(vertexBuffer->map(), 0, vertexBuffer->getSize());
|
||||
vertexBuffer->setMappedRangeModified(0, vertexBuffer->getSize());
|
||||
vertexBuffer->unmap();
|
||||
|
||||
vertexScratchBuffer = new char[vertexStride];
|
||||
}
|
||||
|
||||
Mesh::~Mesh()
|
||||
Mesh::Mesh(const std::vector<Mesh::BufferAttribute> &attributes, PrimitiveType drawmode)
|
||||
: vertexBuffer(nullptr)
|
||||
, vertexCount(0)
|
||||
, vertexStride(0)
|
||||
, vertexScratchBuffer(nullptr)
|
||||
, indexBuffer(nullptr)
|
||||
, useIndexBuffer(false)
|
||||
, indexCount(0)
|
||||
, indexDataType(INDEX_UINT16)
|
||||
, primitiveType(drawmode)
|
||||
, rangeStart(-1)
|
||||
, rangeCount(-1)
|
||||
{
|
||||
delete vertexBuffer;
|
||||
delete indexBuffer;
|
||||
delete vertexScratchBuffer;
|
||||
if (attributes.size() == 0)
|
||||
throw love::Exception("At least one buffer attribute must be specified in this constructor.");
|
||||
|
||||
attachedAttributes = attributes;
|
||||
|
||||
vertexCount = attachedAttributes.size() > 0 ? LOVE_UINT32_MAX : 0;
|
||||
|
||||
for (const auto &attrib : attachedAttributes)
|
||||
{
|
||||
if (attrib.second.mesh != this)
|
||||
attrib.second.mesh->release();
|
||||
if ((attrib.buffer->getTypeFlags() & Buffer::TYPEFLAG_VERTEX) == 0)
|
||||
throw love::Exception("Buffer must be created with vertex buffer support to be used as a Mesh vertex attribute.");
|
||||
|
||||
if (getAttachedAttributeIndex(attrib.name) != -1)
|
||||
throw love::Exception("Duplicate vertex attribute name: %s", attrib.name.c_str());
|
||||
|
||||
vertexCount = std::min(vertexCount, attrib.buffer->getArrayLength());
|
||||
}
|
||||
|
||||
indexDataType = getIndexDataTypeFromMax(vertexCount);
|
||||
}
|
||||
|
||||
Mesh::~Mesh()
|
||||
{
|
||||
delete vertexScratchBuffer;
|
||||
}
|
||||
|
||||
void Mesh::setupAttachedAttributes()
|
||||
{
|
||||
for (size_t i = 0; i < vertexFormat.size(); i++)
|
||||
{
|
||||
const std::string &name = vertexFormat[i].name;
|
||||
const std::string &name = vertexFormat[i].decl.name;
|
||||
|
||||
if (attachedAttributes.find(name) != attachedAttributes.end())
|
||||
if (getAttachedAttributeIndex(name) != -1)
|
||||
throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
|
||||
|
||||
attachedAttributes[name] = {this, (int) i, STEP_PER_VERTEX, true};
|
||||
attachedAttributes.push_back({name, vertexBuffer, (int) i, STEP_PER_VERTEX, true});
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::calculateAttributeSizes(Graphics *gfx)
|
||||
int Mesh::getAttachedAttributeIndex(const std::string &name) const
|
||||
{
|
||||
bool supportsGLSL3 = gfx->getCapabilities().features[Graphics::FEATURE_GLSL3];
|
||||
|
||||
size_t stride = 0;
|
||||
|
||||
for (const AttribFormat &format : vertexFormat)
|
||||
for (int i = 0; i < (int) attachedAttributes.size(); i++)
|
||||
{
|
||||
size_t size = vertex::getDataTypeSize(format.type) * format.components;
|
||||
|
||||
if (format.components <= 0 || format.components > 4)
|
||||
throw love::Exception("Vertex attributes must have between 1 and 4 components.");
|
||||
|
||||
// Hardware really doesn't like attributes that aren't 32 bit-aligned.
|
||||
if (size % 4 != 0)
|
||||
throw love::Exception("Vertex attributes must have enough components to be a multiple of 32 bits.");
|
||||
|
||||
if (vertex::isDataTypeInteger(format.type) && !supportsGLSL3)
|
||||
throw love::Exception("Integer vertex attribute data types require GLSL 3 support.");
|
||||
|
||||
// Total size in bytes of each attribute in a single vertex.
|
||||
attributeSizes.push_back(size);
|
||||
stride += size;
|
||||
if (attachedAttributes[i].name == name)
|
||||
return i;
|
||||
}
|
||||
|
||||
vertexStride = stride;
|
||||
}
|
||||
|
||||
size_t Mesh::getAttributeOffset(size_t attribindex) const
|
||||
{
|
||||
size_t offset = 0;
|
||||
|
||||
for (size_t i = 0; i < attribindex; i++)
|
||||
offset += attributeSizes[i];
|
||||
|
||||
return offset;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize)
|
||||
@@ -186,6 +171,9 @@ void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize)
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
if (vertexBuffer.get() == nullptr)
|
||||
throw love::Exception("setVertex can only be called on a Mesh which owns its own vertex buffer.");
|
||||
|
||||
size_t offset = vertindex * vertexStride;
|
||||
size_t size = std::min(datasize, vertexStride);
|
||||
|
||||
@@ -200,6 +188,9 @@ size_t Mesh::getVertex(size_t vertindex, void *data, size_t datasize)
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
if (vertexBuffer.get() == nullptr)
|
||||
throw love::Exception("getVertex can only be called on a Mesh which owns its own vertex buffer.");
|
||||
|
||||
size_t offset = vertindex * vertexStride;
|
||||
size_t size = std::min(datasize, vertexStride);
|
||||
|
||||
@@ -223,8 +214,13 @@ void Mesh::setVertexAttribute(size_t vertindex, int attribindex, const void *dat
|
||||
if (attribindex >= (int) vertexFormat.size())
|
||||
throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
|
||||
|
||||
size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex);
|
||||
size_t size = std::min(datasize, attributeSizes[attribindex]);
|
||||
if (vertexBuffer.get() == nullptr)
|
||||
throw love::Exception("setVertexAttribute can only be called on a Mesh which owns its own vertex buffer.");
|
||||
|
||||
const auto &member = vertexFormat[attribindex];
|
||||
|
||||
size_t offset = vertindex * vertexStride + member.offset;
|
||||
size_t size = std::min(datasize, member.info.size);
|
||||
|
||||
uint8 *bufferdata = (uint8 *) vertexBuffer->map();
|
||||
memcpy(bufferdata + offset, data, size);
|
||||
@@ -240,8 +236,13 @@ size_t Mesh::getVertexAttribute(size_t vertindex, int attribindex, void *data, s
|
||||
if (attribindex >= (int) vertexFormat.size())
|
||||
throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
|
||||
|
||||
size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex);
|
||||
size_t size = std::min(datasize, attributeSizes[attribindex]);
|
||||
if (vertexBuffer.get() == nullptr)
|
||||
throw love::Exception("getVertexAttribute can only be called on a Mesh which owns its own vertex buffer.");
|
||||
|
||||
const auto &member = vertexFormat[attribindex];
|
||||
|
||||
size_t offset = vertindex * vertexStride + member.offset;
|
||||
size_t size = std::min(datasize, member.info.size);
|
||||
|
||||
// We're relying on map() returning read/write data... ew.
|
||||
const uint8 *bufferdata = (const uint8 *) vertexBuffer->map();
|
||||
@@ -260,128 +261,106 @@ size_t Mesh::getVertexStride() const
|
||||
return vertexStride;
|
||||
}
|
||||
|
||||
const std::vector<Mesh::AttribFormat> &Mesh::getVertexFormat() const
|
||||
Buffer *Mesh::getVertexBuffer() const
|
||||
{
|
||||
return vertexBuffer;
|
||||
}
|
||||
|
||||
const std::vector<Buffer::DataMember> &Mesh::getVertexFormat() const
|
||||
{
|
||||
return vertexFormat;
|
||||
}
|
||||
|
||||
vertex::DataType Mesh::getAttributeInfo(int attribindex, int &components) const
|
||||
{
|
||||
if (attribindex < 0 || attribindex >= (int) vertexFormat.size())
|
||||
throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
|
||||
|
||||
components = vertexFormat[attribindex].components;
|
||||
return vertexFormat[attribindex].type;
|
||||
}
|
||||
|
||||
int Mesh::getAttributeIndex(const std::string &name) const
|
||||
{
|
||||
for (int i = 0; i < (int) vertexFormat.size(); i++)
|
||||
{
|
||||
if (vertexFormat[i].name == name)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Mesh::setAttributeEnabled(const std::string &name, bool enable)
|
||||
{
|
||||
auto it = attachedAttributes.find(name);
|
||||
|
||||
if (it == attachedAttributes.end())
|
||||
int index = getAttachedAttributeIndex(name);
|
||||
if (index == -1)
|
||||
throw love::Exception("Mesh does not have an attached vertex attribute named '%s'", name.c_str());
|
||||
|
||||
it->second.enabled = enable;
|
||||
attachedAttributes[index].enabled = enable;
|
||||
}
|
||||
|
||||
bool Mesh::isAttributeEnabled(const std::string &name) const
|
||||
{
|
||||
const auto it = attachedAttributes.find(name);
|
||||
|
||||
if (it == attachedAttributes.end())
|
||||
int index = getAttachedAttributeIndex(name);
|
||||
if (index == -1)
|
||||
throw love::Exception("Mesh does not have an attached vertex attribute named '%s'", name.c_str());
|
||||
|
||||
return it->second.enabled;
|
||||
return attachedAttributes[index].enabled;
|
||||
}
|
||||
|
||||
void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname, AttributeStep step)
|
||||
void Mesh::attachAttribute(const std::string &name, Buffer *buffer, const std::string &attachname, AttributeStep step)
|
||||
{
|
||||
if ((buffer->getTypeFlags() & Buffer::TYPEFLAG_VERTEX) == 0)
|
||||
throw love::Exception("Buffer must be created with vertex buffer support to be used as a Mesh vertex attribute.");
|
||||
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
if (step == STEP_PER_INSTANCE && !gfx->getCapabilities().features[Graphics::FEATURE_INSTANCING])
|
||||
throw love::Exception("Vertex attribute instancing is not supported on this system.");
|
||||
|
||||
if (mesh != this)
|
||||
{
|
||||
for (const auto &it : mesh->attachedAttributes)
|
||||
{
|
||||
// If the supplied Mesh has attached attributes of its own, then we
|
||||
// prevent it from being attached to avoid reference cycles.
|
||||
if (it.second.mesh != mesh)
|
||||
throw love::Exception("Cannot attach a Mesh which has attached Meshes of its own.");
|
||||
}
|
||||
}
|
||||
BufferAttribute oldattrib = {};
|
||||
BufferAttribute newattrib = {};
|
||||
|
||||
AttachedAttribute oldattrib = {};
|
||||
AttachedAttribute newattrib = {};
|
||||
int oldindex = getAttachedAttributeIndex(name);
|
||||
if (oldindex != -1)
|
||||
oldattrib = attachedAttributes[oldindex];
|
||||
else if (attachedAttributes.size() + 1 > VertexAttributes::MAX)
|
||||
throw love::Exception("A maximum of %d attributes can be attached at once.", VertexAttributes::MAX);
|
||||
|
||||
auto it = attachedAttributes.find(name);
|
||||
if (it != attachedAttributes.end())
|
||||
oldattrib = it->second;
|
||||
else if (attachedAttributes.size() + 1 > vertex::Attributes::MAX)
|
||||
throw love::Exception("A maximum of %d attributes can be attached at once.", vertex::Attributes::MAX);
|
||||
|
||||
newattrib.mesh = mesh;
|
||||
newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true;
|
||||
newattrib.index = mesh->getAttributeIndex(attachname);
|
||||
newattrib.name = name;
|
||||
newattrib.buffer = buffer;
|
||||
newattrib.enabled = oldattrib.buffer.get() ? oldattrib.enabled : true;
|
||||
newattrib.indexInBuffer = buffer->getDataMemberIndex(attachname);
|
||||
newattrib.step = step;
|
||||
|
||||
if (newattrib.index < 0)
|
||||
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", attachname.c_str());
|
||||
if (newattrib.indexInBuffer < 0)
|
||||
throw love::Exception("The specified vertex buffer does not have a vertex attribute named '%s'", attachname.c_str());
|
||||
|
||||
if (newattrib.mesh != this)
|
||||
newattrib.mesh->retain();
|
||||
|
||||
attachedAttributes[name] = newattrib;
|
||||
|
||||
if (oldattrib.mesh && oldattrib.mesh != this)
|
||||
oldattrib.mesh->release();
|
||||
if (oldindex != -1)
|
||||
attachedAttributes[oldindex] = newattrib;
|
||||
else
|
||||
attachedAttributes.push_back(newattrib);
|
||||
}
|
||||
|
||||
bool Mesh::detachAttribute(const std::string &name)
|
||||
{
|
||||
auto it = attachedAttributes.find(name);
|
||||
int index = getAttachedAttributeIndex(name);
|
||||
if (index == -1)
|
||||
return false;
|
||||
|
||||
if (it != attachedAttributes.end() && it->second.mesh != this)
|
||||
{
|
||||
it->second.mesh->release();
|
||||
attachedAttributes.erase(it);
|
||||
attachedAttributes.erase(attachedAttributes.begin() + index);
|
||||
|
||||
if (getAttributeIndex(name) != -1)
|
||||
attachAttribute(name, this, name);
|
||||
if (vertexBuffer.get() && vertexBuffer->getDataMemberIndex(name) != -1)
|
||||
attachAttribute(name, vertexBuffer, name);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
const std::vector<Mesh::BufferAttribute> &Mesh::getAttachedAttributes() const
|
||||
{
|
||||
return attachedAttributes;
|
||||
}
|
||||
|
||||
void *Mesh::mapVertexData()
|
||||
{
|
||||
return vertexBuffer->map();
|
||||
return vertexBuffer.get() != nullptr ? vertexBuffer->map() : nullptr;
|
||||
}
|
||||
|
||||
void Mesh::unmapVertexData(size_t modifiedoffset, size_t modifiedsize)
|
||||
{
|
||||
if (!vertexBuffer.get())
|
||||
return;
|
||||
|
||||
vertexBuffer->setMappedRangeModified(modifiedoffset, modifiedsize);
|
||||
vertexBuffer->unmap();
|
||||
}
|
||||
|
||||
void Mesh::flush()
|
||||
{
|
||||
vertexBuffer->unmap();
|
||||
if (vertexBuffer.get())
|
||||
vertexBuffer->unmap();
|
||||
|
||||
if (indexBuffer != nullptr)
|
||||
if (indexBuffer.get())
|
||||
indexBuffer->unmap();
|
||||
}
|
||||
|
||||
@@ -391,7 +370,7 @@ void Mesh::flush()
|
||||
template <typename T>
|
||||
static void copyToIndexBuffer(const std::vector<uint32> &indices, Buffer::Mapper &buffermap, size_t maxval)
|
||||
{
|
||||
T *elems = (T *) buffermap.get();
|
||||
T *elems = (T *) buffermap.data;
|
||||
|
||||
for (size_t i = 0; i < indices.size(); i++)
|
||||
{
|
||||
@@ -406,21 +385,18 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
|
||||
{
|
||||
size_t maxval = getVertexCount();
|
||||
|
||||
IndexDataType datatype = vertex::getIndexDataTypeFromMax(maxval);
|
||||
IndexDataType datatype = getIndexDataTypeFromMax(maxval);
|
||||
DataFormat dataformat = getIndexDataFormat(datatype);
|
||||
|
||||
// Calculate the size in bytes of the index buffer data.
|
||||
size_t size = map.size() * vertex::getIndexDataSize(datatype);
|
||||
size_t size = map.size() * getIndexDataSize(datatype);
|
||||
|
||||
if (indexBuffer && size > indexBuffer->getSize())
|
||||
{
|
||||
delete indexBuffer;
|
||||
indexBuffer = nullptr;
|
||||
}
|
||||
|
||||
if (!indexBuffer && size > 0)
|
||||
if (indexBuffer.get() == nullptr || size > indexBuffer->getSize() || indexBuffer->getDataMember(0).decl.format != dataformat)
|
||||
{
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
indexBuffer = gfx->newBuffer(size, nullptr, BUFFER_INDEX, vertexBuffer->getUsage(), Buffer::MAP_READ);
|
||||
auto usage = vertexBuffer.get() ? vertexBuffer->getUsage() : BUFFERUSAGE_DYNAMIC;
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_READ, usage);
|
||||
indexBuffer.set(gfx->newBuffer(settings, dataformat, nullptr, size, 0), Acquire::NORETAIN);
|
||||
}
|
||||
|
||||
useIndexBuffer = true;
|
||||
@@ -448,25 +424,23 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
|
||||
|
||||
void Mesh::setVertexMap(IndexDataType datatype, const void *data, size_t datasize)
|
||||
{
|
||||
if (indexBuffer && datasize > indexBuffer->getSize())
|
||||
{
|
||||
delete indexBuffer;
|
||||
indexBuffer = nullptr;
|
||||
}
|
||||
DataFormat dataformat = getIndexDataFormat(datatype);
|
||||
|
||||
if (!indexBuffer && datasize > 0)
|
||||
if (indexBuffer.get() == nullptr || datasize > indexBuffer->getSize() || indexBuffer->getDataMember(0).decl.format != dataformat)
|
||||
{
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
indexBuffer = gfx->newBuffer(datasize, nullptr, BUFFER_INDEX, vertexBuffer->getUsage(), Buffer::MAP_READ);
|
||||
auto usage = vertexBuffer.get() ? vertexBuffer->getUsage() : BUFFERUSAGE_DYNAMIC;
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_READ, usage);
|
||||
indexBuffer.set(gfx->newBuffer(settings, dataformat, nullptr, datasize, 0), Acquire::NORETAIN);
|
||||
}
|
||||
|
||||
indexCount = datasize / vertex::getIndexDataSize(datatype);
|
||||
indexCount = datasize / getIndexDataSize(datatype);
|
||||
|
||||
if (!indexBuffer || indexCount == 0)
|
||||
return;
|
||||
|
||||
Buffer::Mapper ibomap(*indexBuffer);
|
||||
memcpy(ibomap.get(), data, datasize);
|
||||
memcpy(ibomap.data, data, datasize);
|
||||
|
||||
useIndexBuffer = true;
|
||||
indexDataType = datatype;
|
||||
@@ -499,6 +473,9 @@ bool Mesh::getVertexMap(std::vector<uint32> &map) const
|
||||
if (!indexBuffer || indexCount == 0)
|
||||
return true;
|
||||
|
||||
if ((indexBuffer->getMapFlags() & Buffer::MAP_READ) == 0)
|
||||
return false;
|
||||
|
||||
// We unmap the buffer in Mesh::draw, Mesh::setVertexMap, and Mesh::flush.
|
||||
void *buffer = indexBuffer->map();
|
||||
|
||||
@@ -517,7 +494,27 @@ bool Mesh::getVertexMap(std::vector<uint32> &map) const
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Mesh::getVertexMapCount() const
|
||||
void Mesh::setIndexBuffer(Buffer *buffer)
|
||||
{
|
||||
// Buffer constructor does the rest of the validation for index buffers
|
||||
// (data member formats, etc.)
|
||||
if (buffer != nullptr && (buffer->getTypeFlags() & Buffer::TYPEFLAG_INDEX) == 0)
|
||||
throw love::Exception("setIndexBuffer requires a Buffer created as an index buffer.");
|
||||
|
||||
indexBuffer.set(buffer);
|
||||
useIndexBuffer = buffer != nullptr;
|
||||
indexCount = buffer != nullptr ? buffer->getArrayLength() : 0;
|
||||
|
||||
if (buffer != nullptr)
|
||||
indexDataType = getIndexDataType(buffer->getDataMember(0).decl.format);
|
||||
}
|
||||
|
||||
Buffer *Mesh::getIndexBuffer() const
|
||||
{
|
||||
return indexBuffer;
|
||||
}
|
||||
|
||||
size_t Mesh::getIndexCount() const
|
||||
{
|
||||
return indexCount;
|
||||
}
|
||||
@@ -592,43 +589,42 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
|
||||
if (Shader::current && texture.get())
|
||||
Shader::current->checkMainTexture(texture);
|
||||
|
||||
vertex::Attributes attributes;
|
||||
vertex::BufferBindings buffers;
|
||||
VertexAttributes attributes;
|
||||
BufferBindings buffers;
|
||||
|
||||
int activebuffers = 0;
|
||||
|
||||
for (const auto &attrib : attachedAttributes)
|
||||
{
|
||||
if (!attrib.second.enabled)
|
||||
if (!attrib.enabled)
|
||||
continue;
|
||||
|
||||
Mesh *mesh = attrib.second.mesh;
|
||||
Buffer *buffer = attrib.buffer.get();
|
||||
int attributeindex = -1;
|
||||
|
||||
// If the attribute is one of the LOVE-defined ones, use the constant
|
||||
// attribute index for it, otherwise query the index from the shader.
|
||||
BuiltinVertexAttribute builtinattrib;
|
||||
if (vertex::getConstant(attrib.first.c_str(), builtinattrib))
|
||||
if (getConstant(attrib.name.c_str(), builtinattrib))
|
||||
attributeindex = (int) builtinattrib;
|
||||
else if (Shader::current)
|
||||
attributeindex = Shader::current->getVertexAttributeIndex(attrib.first);
|
||||
attributeindex = Shader::current->getVertexAttributeIndex(attrib.name);
|
||||
|
||||
if (attributeindex >= 0)
|
||||
{
|
||||
// Make sure the buffer isn't mapped (sends data to GPU if needed.)
|
||||
mesh->vertexBuffer->unmap();
|
||||
buffer->unmap();
|
||||
|
||||
const auto &formats = mesh->getVertexFormat();
|
||||
const auto &format = formats[attrib.second.index];
|
||||
const auto &member = buffer->getDataMember(attrib.indexInBuffer);
|
||||
|
||||
uint16 offset = (uint16) mesh->getAttributeOffset(attrib.second.index);
|
||||
uint16 stride = (uint16) mesh->getVertexStride();
|
||||
uint16 offset = (uint16) member.offset;
|
||||
uint16 stride = (uint16) buffer->getArrayStride();
|
||||
|
||||
attributes.set(attributeindex, format.type, (uint8) format.components, offset, activebuffers);
|
||||
attributes.setBufferLayout(activebuffers, stride, attrib.second.step);
|
||||
attributes.set(attributeindex, member.decl.format, offset, activebuffers);
|
||||
attributes.setBufferLayout(activebuffers, stride, attrib.step);
|
||||
|
||||
// TODO: Ideally we want to reuse buffers with the same stride+step.
|
||||
buffers.set(activebuffers, mesh->vertexBuffer, 0);
|
||||
buffers.set(activebuffers, buffer, 0);
|
||||
activebuffers++;
|
||||
}
|
||||
}
|
||||
@@ -653,7 +649,7 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
|
||||
cmd.cullMode = gfx->getMeshCullMode();
|
||||
|
||||
int start = std::min(std::max(0, rangeStart), (int) indexCount - 1);
|
||||
cmd.indexBufferOffset = start * vertex::getIndexDataSize(indexDataType);
|
||||
cmd.indexBufferOffset = start * getIndexDataSize(indexDataType);
|
||||
|
||||
cmd.indexCount = (int) indexCount;
|
||||
if (rangeCount > 0)
|
||||
|
||||
+30
-30
@@ -50,17 +50,20 @@ class Mesh : public Drawable
|
||||
{
|
||||
public:
|
||||
|
||||
static love::Type type;
|
||||
|
||||
struct AttribFormat
|
||||
struct BufferAttribute
|
||||
{
|
||||
std::string name;
|
||||
vertex::DataType type;
|
||||
int components; // max 4
|
||||
StrongRef<Buffer> buffer;
|
||||
int indexInBuffer;
|
||||
AttributeStep step;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
Mesh(Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, vertex::Usage usage);
|
||||
Mesh(Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, PrimitiveType drawmode, vertex::Usage usage);
|
||||
static love::Type type;
|
||||
|
||||
Mesh(Graphics *gfx, const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage);
|
||||
Mesh(Graphics *gfx, const std::vector<Buffer::DataDeclaration> &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage);
|
||||
Mesh(const std::vector<BufferAttribute> &attributes, PrimitiveType drawmode);
|
||||
|
||||
virtual ~Mesh();
|
||||
|
||||
@@ -92,12 +95,15 @@ public:
|
||||
**/
|
||||
size_t getVertexStride() const;
|
||||
|
||||
/**
|
||||
* Gets the Buffer that holds the Mesh's vertices.
|
||||
**/
|
||||
Buffer *getVertexBuffer() const;
|
||||
|
||||
/**
|
||||
* Gets the format of each vertex attribute stored in the Mesh.
|
||||
**/
|
||||
const std::vector<AttribFormat> &getVertexFormat() const;
|
||||
vertex::DataType getAttributeInfo(int attribindex, int &components) const;
|
||||
int getAttributeIndex(const std::string &name) const;
|
||||
const std::vector<Buffer::DataMember> &getVertexFormat() const;
|
||||
|
||||
/**
|
||||
* Sets whether a specific vertex attribute is used when drawing the Mesh.
|
||||
@@ -106,11 +112,12 @@ public:
|
||||
bool isAttributeEnabled(const std::string &name) const;
|
||||
|
||||
/**
|
||||
* Attaches a vertex attribute from another Mesh to this one. The attribute
|
||||
* will be used when drawing this Mesh.
|
||||
* Attaches a vertex attribute from another vertex buffer to this Mesh. The
|
||||
* attribute will be used when drawing this Mesh.
|
||||
**/
|
||||
void attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX);
|
||||
void attachAttribute(const std::string &name, Buffer *buffer, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX);
|
||||
bool detachAttribute(const std::string &name);
|
||||
const std::vector<BufferAttribute> &getAttachedAttributes() const;
|
||||
|
||||
void *mapVertexData();
|
||||
void unmapVertexData(size_t modifiedoffset = 0, size_t modifiedsize = -1);
|
||||
@@ -136,10 +143,13 @@ public:
|
||||
**/
|
||||
bool getVertexMap(std::vector<uint32> &map) const;
|
||||
|
||||
void setIndexBuffer(Buffer *buffer);
|
||||
Buffer *getIndexBuffer() const;
|
||||
|
||||
/**
|
||||
* Gets the total number of elements in the vertex map array.
|
||||
**/
|
||||
size_t getVertexMapCount() const;
|
||||
size_t getIndexCount() const;
|
||||
|
||||
/**
|
||||
* Sets the texture used when drawing the Mesh.
|
||||
@@ -172,31 +182,21 @@ public:
|
||||
|
||||
void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount);
|
||||
|
||||
static std::vector<AttribFormat> getDefaultVertexFormat();
|
||||
static std::vector<Buffer::DataDeclaration> getDefaultVertexFormat();
|
||||
|
||||
private:
|
||||
|
||||
friend class SpriteBatch;
|
||||
|
||||
struct AttachedAttribute
|
||||
{
|
||||
Mesh *mesh;
|
||||
int index;
|
||||
AttributeStep step;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
void setupAttachedAttributes();
|
||||
void calculateAttributeSizes(Graphics *gfx);
|
||||
size_t getAttributeOffset(size_t attribindex) const;
|
||||
int getAttachedAttributeIndex(const std::string &name) const;
|
||||
|
||||
std::vector<AttribFormat> vertexFormat;
|
||||
std::vector<size_t> attributeSizes;
|
||||
std::vector<Buffer::DataMember> vertexFormat;
|
||||
|
||||
std::unordered_map<std::string, AttachedAttribute> attachedAttributes;
|
||||
std::vector<BufferAttribute> attachedAttributes;
|
||||
|
||||
// Vertex buffer, for the vertex data.
|
||||
Buffer *vertexBuffer;
|
||||
StrongRef<Buffer> vertexBuffer;
|
||||
size_t vertexCount;
|
||||
size_t vertexStride;
|
||||
|
||||
@@ -205,7 +205,7 @@ private:
|
||||
char *vertexScratchBuffer;
|
||||
|
||||
// Index buffer, for the vertex map.
|
||||
Buffer *indexBuffer;
|
||||
StrongRef<Buffer> indexBuffer;
|
||||
bool useIndexBuffer;
|
||||
size_t indexCount;
|
||||
IndexDataType indexDataType;
|
||||
|
||||
@@ -93,7 +93,7 @@ ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
|
||||
, offset(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f)
|
||||
, defaultOffset(true)
|
||||
, relativeRotation(false)
|
||||
, vertexAttributes(vertex::CommonFormat::XYf_STf_RGBAub, 0)
|
||||
, vertexAttributes(CommonFormat::XYf_STf_RGBAub, 0)
|
||||
, buffer(nullptr)
|
||||
{
|
||||
if (size == 0 || size > MAX_PARTICLES)
|
||||
@@ -191,7 +191,9 @@ void ParticleSystem::createBuffers(size_t size)
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
|
||||
size_t bytes = sizeof(Vertex) * size * 4;
|
||||
buffer = gfx->newBuffer(bytes, nullptr, BUFFER_VERTEX, vertex::USAGE_STREAM, 0);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, 0, BUFFERUSAGE_STREAM);
|
||||
auto decl = Buffer::getCommonFormatDeclaration(CommonFormat::XYf_STf_RGBAub);
|
||||
buffer = gfx->newBuffer(settings, decl, nullptr, bytes, 0);
|
||||
}
|
||||
catch (std::bad_alloc &)
|
||||
{
|
||||
@@ -203,7 +205,8 @@ void ParticleSystem::createBuffers(size_t size)
|
||||
void ParticleSystem::deleteBuffers()
|
||||
{
|
||||
delete[] pMem;
|
||||
delete buffer;
|
||||
if (buffer)
|
||||
buffer->release();
|
||||
|
||||
pMem = nullptr;
|
||||
buffer = nullptr;
|
||||
@@ -1080,7 +1083,7 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
|
||||
|
||||
Graphics::TempTransform transform(gfx, m);
|
||||
|
||||
vertex::BufferBindings vertexbuffers;
|
||||
BufferBindings vertexbuffers;
|
||||
vertexbuffers.set(0, buffer, 0);
|
||||
|
||||
gfx->drawQuads(0, pCount, vertexAttributes, vertexbuffers, texture);
|
||||
|
||||
@@ -673,7 +673,7 @@ private:
|
||||
|
||||
bool relativeRotation;
|
||||
|
||||
const vertex::Attributes vertexAttributes;
|
||||
const VertexAttributes vertexAttributes;
|
||||
Buffer *buffer;
|
||||
|
||||
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM>::Entry distributionsEntries[];
|
||||
|
||||
@@ -82,7 +82,7 @@ void Polyline::render(const Vector2 *coords, size_t count, size_t size_hint, flo
|
||||
// extra degenerate triangle in between the core line and the overdraw
|
||||
// line in order to break up the strip into two. This will let us draw
|
||||
// everything in one draw call.
|
||||
if (triangle_mode == vertex::TriangleIndexMode::STRIP)
|
||||
if (triangle_mode == TRIANGLEINDEX_STRIP)
|
||||
extra_vertices = 2;
|
||||
}
|
||||
|
||||
@@ -383,7 +383,7 @@ void Polyline::draw(love::graphics::Graphics *gfx)
|
||||
int maxvertices = LOVE_UINT16_MAX - 3;
|
||||
|
||||
int advance = maxvertices;
|
||||
if (triangle_mode == vertex::TriangleIndexMode::STRIP)
|
||||
if (triangle_mode == TRIANGLEINDEX_STRIP)
|
||||
advance -= 2;
|
||||
|
||||
for (int vertex_start = 0; vertex_start < total_vertex_count; vertex_start += advance)
|
||||
@@ -391,8 +391,8 @@ void Polyline::draw(love::graphics::Graphics *gfx)
|
||||
const Vector2 *verts = vertices + vertex_start;
|
||||
|
||||
Graphics::BatchedDrawCommand cmd;
|
||||
cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
|
||||
cmd.formats[1] = vertex::CommonFormat::RGBAub;
|
||||
cmd.formats[0] = getSinglePositionFormat(is2D);
|
||||
cmd.formats[1] = CommonFormat::RGBAub;
|
||||
cmd.indexMode = triangle_mode;
|
||||
cmd.vertexCount = std::min(maxvertices, total_vertex_count - vertex_start);
|
||||
|
||||
@@ -405,18 +405,28 @@ void Polyline::draw(love::graphics::Graphics *gfx)
|
||||
|
||||
Color32 *colordata = (Color32 *) data.stream[1];
|
||||
|
||||
int draw_rough_count = std::min(cmd.vertexCount, (int) vertex_count - vertex_start);
|
||||
|
||||
// Constant vertex color up to the overdraw vertices.
|
||||
for (int i = 0; i < std::min(cmd.vertexCount, (int) vertex_count - vertex_start); i++)
|
||||
for (int i = 0; i < draw_rough_count; i++)
|
||||
colordata[i] = curcolor;
|
||||
|
||||
int colorcount = 0;
|
||||
if (overdraw)
|
||||
colorcount = std::min(cmd.vertexCount, overdraw_count - (vertex_start - overdraw_start));
|
||||
|
||||
if (colorcount > 0)
|
||||
{
|
||||
Color32 *colors = colordata + std::max(0, (overdraw_start - vertex_start));
|
||||
fill_color_array(curcolor, colors, colorcount);
|
||||
int draw_remaining_count = cmd.vertexCount - draw_rough_count;
|
||||
|
||||
int draw_overdraw_begin = overdraw_start - vertex_start;
|
||||
int draw_overdraw_end = draw_overdraw_begin + overdraw_count;
|
||||
|
||||
draw_overdraw_begin = std::max(0, draw_overdraw_begin);
|
||||
|
||||
int draw_overdraw_count = std::min(draw_remaining_count, draw_overdraw_end - draw_overdraw_begin);
|
||||
|
||||
if (draw_overdraw_count > 0)
|
||||
{
|
||||
Color32 *colors = colordata + draw_overdraw_begin;
|
||||
fill_color_array(curcolor, colors, draw_overdraw_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class Polyline
|
||||
{
|
||||
public:
|
||||
|
||||
Polyline(vertex::TriangleIndexMode mode = vertex::TriangleIndexMode::STRIP)
|
||||
Polyline(TriangleIndexMode mode = TRIANGLEINDEX_STRIP)
|
||||
: vertices(nullptr)
|
||||
, overdraw(nullptr)
|
||||
, vertex_count(0)
|
||||
@@ -94,7 +94,7 @@ protected:
|
||||
Vector2 *overdraw;
|
||||
size_t vertex_count;
|
||||
size_t overdraw_vertex_count;
|
||||
vertex::TriangleIndexMode triangle_mode;
|
||||
TriangleIndexMode triangle_mode;
|
||||
size_t overdraw_vertex_start;
|
||||
|
||||
}; // Polyline
|
||||
@@ -109,7 +109,7 @@ class NoneJoinPolyline : public Polyline
|
||||
public:
|
||||
|
||||
NoneJoinPolyline()
|
||||
: Polyline(vertex::TriangleIndexMode::QUADS)
|
||||
: Polyline(TRIANGLEINDEX_QUADS)
|
||||
{}
|
||||
|
||||
void render(const Vector2 *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
|
||||
|
||||
+511
-22
@@ -28,12 +28,402 @@
|
||||
|
||||
// C++
|
||||
#include <string>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
namespace glsl
|
||||
{
|
||||
static const char global_syntax[] = R"(
|
||||
#if !defined(GL_ES) && __VERSION__ < 140
|
||||
#define lowp
|
||||
#define mediump
|
||||
#define highp
|
||||
#endif
|
||||
#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH)
|
||||
#define LOVE_HIGHP_OR_MEDIUMP highp
|
||||
#else
|
||||
#define LOVE_HIGHP_OR_MEDIUMP mediump
|
||||
#endif
|
||||
#define number float
|
||||
#define Image sampler2D
|
||||
#define ArrayImage sampler2DArray
|
||||
#define CubeImage samplerCube
|
||||
#define VolumeImage sampler3D
|
||||
#if __VERSION__ >= 300 && !defined(LOVE_GLSL1_ON_GLSL3)
|
||||
#define DepthImage sampler2DShadow
|
||||
#define DepthArrayImage sampler2DArrayShadow
|
||||
#define DepthCubeImage samplerCubeShadow
|
||||
#endif
|
||||
#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
|
||||
#ifdef GL_OES_standard_derivatives
|
||||
#extension GL_OES_standard_derivatives : enable
|
||||
#endif
|
||||
)";
|
||||
|
||||
static const char global_uniforms[] = R"(
|
||||
// According to the GLSL ES 1.0 spec, uniform precision must match between stages,
|
||||
// but we can't guarantee that highp is always supported in fragment shaders...
|
||||
// We *really* don't want to use mediump for these in vertex shaders though.
|
||||
uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13];
|
||||
|
||||
// These are initialized in love_initializeBuiltinUniforms below. GLSL ES can't
|
||||
// do it as an initializer.
|
||||
LOVE_HIGHP_OR_MEDIUMP mat4 TransformMatrix;
|
||||
LOVE_HIGHP_OR_MEDIUMP mat4 ProjectionMatrix;
|
||||
LOVE_HIGHP_OR_MEDIUMP mat3 NormalMatrix;
|
||||
|
||||
LOVE_HIGHP_OR_MEDIUMP vec4 love_ScreenSize;
|
||||
LOVE_HIGHP_OR_MEDIUMP vec4 ConstantColor;
|
||||
|
||||
#define TransformProjectionMatrix (ProjectionMatrix * TransformMatrix)
|
||||
|
||||
// Alternate names
|
||||
#define ViewSpaceFromLocal TransformMatrix
|
||||
#define ClipSpaceFromView ProjectionMatrix
|
||||
#define ClipSpaceFromLocal TransformProjectionMatrix
|
||||
#define ViewNormalFromLocal NormalMatrix
|
||||
|
||||
void love_initializeBuiltinUniforms() {
|
||||
TransformMatrix = mat4(
|
||||
love_UniformsPerDraw[0],
|
||||
love_UniformsPerDraw[1],
|
||||
love_UniformsPerDraw[2],
|
||||
love_UniformsPerDraw[3]
|
||||
);
|
||||
|
||||
ProjectionMatrix = mat4(
|
||||
love_UniformsPerDraw[4],
|
||||
love_UniformsPerDraw[5],
|
||||
love_UniformsPerDraw[6],
|
||||
love_UniformsPerDraw[7]
|
||||
);
|
||||
|
||||
NormalMatrix = mat3(
|
||||
love_UniformsPerDraw[8].xyz,
|
||||
love_UniformsPerDraw[9].xyz,
|
||||
love_UniformsPerDraw[10].xyz
|
||||
);
|
||||
|
||||
love_ScreenSize = love_UniformsPerDraw[11];
|
||||
ConstantColor = love_UniformsPerDraw[12];
|
||||
}
|
||||
)";
|
||||
|
||||
static const char global_functions[] = R"(
|
||||
#ifdef GL_ES
|
||||
#if __VERSION__ >= 300 || defined(GL_EXT_texture_array)
|
||||
precision lowp sampler2DArray;
|
||||
#endif
|
||||
#if __VERSION__ >= 300 || defined(GL_OES_texture_3D)
|
||||
precision lowp sampler3D;
|
||||
#endif
|
||||
#if __VERSION__ >= 300
|
||||
precision lowp sampler2DShadow;
|
||||
precision lowp samplerCubeShadow;
|
||||
precision lowp sampler2DArrayShadow;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if __VERSION__ >= 130 && !defined(LOVE_GLSL1_ON_GLSL3)
|
||||
#define Texel texture
|
||||
#else
|
||||
#if __VERSION__ >= 130
|
||||
#define texture2D Texel
|
||||
#define texture3D Texel
|
||||
#define textureCube Texel
|
||||
#define texture2DArray Texel
|
||||
#define love_texture2D texture
|
||||
#define love_texture3D texture
|
||||
#define love_textureCube texture
|
||||
#define love_texture2DArray texture
|
||||
#else
|
||||
#define love_texture2D texture2D
|
||||
#define love_texture3D texture3D
|
||||
#define love_textureCube textureCube
|
||||
#define love_texture2DArray texture2DArray
|
||||
#endif
|
||||
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
|
||||
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
|
||||
#define texture love_texture
|
||||
#endif
|
||||
|
||||
float gammaToLinearPrecise(float c) {
|
||||
return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4);
|
||||
}
|
||||
vec3 gammaToLinearPrecise(vec3 c) {
|
||||
bvec3 leq = lessThanEqual(c, vec3(0.04045));
|
||||
c.r = leq.r ? c.r / 12.92 : pow((c.r + 0.055) / 1.055, 2.4);
|
||||
c.g = leq.g ? c.g / 12.92 : pow((c.g + 0.055) / 1.055, 2.4);
|
||||
c.b = leq.b ? c.b / 12.92 : pow((c.b + 0.055) / 1.055, 2.4);
|
||||
return c;
|
||||
}
|
||||
vec4 gammaToLinearPrecise(vec4 c) { return vec4(gammaToLinearPrecise(c.rgb), c.a); }
|
||||
float linearToGammaPrecise(float c) {
|
||||
return c < 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1.0 / 2.4) - 0.055;
|
||||
}
|
||||
vec3 linearToGammaPrecise(vec3 c) {
|
||||
bvec3 lt = lessThanEqual(c, vec3(0.0031308));
|
||||
c.r = lt.r ? c.r * 12.92 : 1.055 * pow(c.r, 1.0 / 2.4) - 0.055;
|
||||
c.g = lt.g ? c.g * 12.92 : 1.055 * pow(c.g, 1.0 / 2.4) - 0.055;
|
||||
c.b = lt.b ? c.b * 12.92 : 1.055 * pow(c.b, 1.0 / 2.4) - 0.055;
|
||||
return c;
|
||||
}
|
||||
vec4 linearToGammaPrecise(vec4 c) { return vec4(linearToGammaPrecise(c.rgb), c.a); }
|
||||
|
||||
// http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1
|
||||
|
||||
mediump float gammaToLinearFast(mediump float c) { return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); }
|
||||
mediump vec3 gammaToLinearFast(mediump vec3 c) { return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); }
|
||||
mediump vec4 gammaToLinearFast(mediump vec4 c) { return vec4(gammaToLinearFast(c.rgb), c.a); }
|
||||
|
||||
mediump float linearToGammaFast(mediump float c) { return max(1.055 * pow(max(c, 0.0), 0.41666666) - 0.055, 0.0); }
|
||||
mediump vec3 linearToGammaFast(mediump vec3 c) { return max(1.055 * pow(max(c, vec3(0.0)), vec3(0.41666666)) - 0.055, vec3(0.0)); }
|
||||
mediump vec4 linearToGammaFast(mediump vec4 c) { return vec4(linearToGammaFast(c.rgb), c.a); }
|
||||
|
||||
#define gammaToLinear gammaToLinearFast
|
||||
#define linearToGamma linearToGammaFast
|
||||
|
||||
#ifdef LOVE_GAMMA_CORRECT
|
||||
#define gammaCorrectColor gammaToLinear
|
||||
#define unGammaCorrectColor linearToGamma
|
||||
#define gammaCorrectColorPrecise gammaToLinearPrecise
|
||||
#define unGammaCorrectColorPrecise linearToGammaPrecise
|
||||
#define gammaCorrectColorFast gammaToLinearFast
|
||||
#define unGammaCorrectColorFast linearToGammaFast
|
||||
#else
|
||||
#define gammaCorrectColor
|
||||
#define unGammaCorrectColor
|
||||
#define gammaCorrectColorPrecise
|
||||
#define unGammaCorrectColorPrecise
|
||||
#define gammaCorrectColorFast
|
||||
#define unGammaCorrectColorFast
|
||||
#endif
|
||||
)";
|
||||
|
||||
static const char vertex_header[] = R"(
|
||||
#define love_Position gl_Position
|
||||
|
||||
#if __VERSION__ >= 130
|
||||
#define attribute in
|
||||
#define varying out
|
||||
#ifndef LOVE_GLSL1_ON_GLSL3
|
||||
#define love_VertexID gl_VertexID
|
||||
#define love_InstanceID gl_InstanceID
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef GL_ES
|
||||
uniform mediump float love_PointSize;
|
||||
#endif
|
||||
)";
|
||||
|
||||
static const char vertex_functions[] = R"(
|
||||
void setPointSize() {
|
||||
#ifdef GL_ES
|
||||
gl_PointSize = love_PointSize;
|
||||
#endif
|
||||
}
|
||||
)";
|
||||
|
||||
static const char vertex_main[] = R"(
|
||||
attribute vec4 VertexPosition;
|
||||
attribute vec4 VertexTexCoord;
|
||||
attribute vec4 VertexColor;
|
||||
|
||||
varying vec4 VaryingTexCoord;
|
||||
varying vec4 VaryingColor;
|
||||
|
||||
vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition);
|
||||
|
||||
void main() {
|
||||
love_initializeBuiltinUniforms();
|
||||
VaryingTexCoord = VertexTexCoord;
|
||||
VaryingColor = gammaCorrectColor(VertexColor) * ConstantColor;
|
||||
setPointSize();
|
||||
love_Position = position(ClipSpaceFromLocal, VertexPosition);
|
||||
}
|
||||
)";
|
||||
|
||||
static const char pixel_header[] = R"(
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
#define love_MaxRenderTargets gl_MaxDrawBuffers
|
||||
|
||||
#if __VERSION__ >= 130
|
||||
#define varying in
|
||||
// Some drivers seem to make the pixel shader do more work when multiple
|
||||
// pixel shader outputs are defined, even when only one is actually used.
|
||||
// TODO: We should use reflection or something instead of this, to determine
|
||||
// how many outputs are actually used in the shader code.
|
||||
#ifdef LOVE_MULTI_RENDER_TARGETS
|
||||
layout(location = 0) out vec4 love_RenderTargets[love_MaxRenderTargets];
|
||||
#define love_PixelColor love_RenderTargets[0]
|
||||
#else
|
||||
layout(location = 0) out vec4 love_PixelColor;
|
||||
#endif
|
||||
#else
|
||||
#ifdef LOVE_MULTI_RENDER_TARGETS
|
||||
#define love_RenderTargets gl_FragData
|
||||
#endif
|
||||
#define love_PixelColor gl_FragColor
|
||||
#endif
|
||||
|
||||
// Legacy
|
||||
#define love_MaxCanvases love_MaxRenderTargets
|
||||
#define love_Canvases love_RenderTargets
|
||||
#ifdef LOVE_MULTI_RENDER_TARGETS
|
||||
#define LOVE_MULTI_CANVASES 1
|
||||
#endif
|
||||
|
||||
// See Shader::updateScreenParams in Shader.cpp.
|
||||
#define love_PixelCoord (vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w))
|
||||
)";
|
||||
|
||||
static const char pixel_functions[] = R"(
|
||||
uniform sampler2D love_VideoYChannel;
|
||||
uniform sampler2D love_VideoCbChannel;
|
||||
uniform sampler2D love_VideoCrChannel;
|
||||
|
||||
vec4 VideoTexel(vec2 texcoords) {
|
||||
vec3 yuv;
|
||||
yuv[0] = Texel(love_VideoYChannel, texcoords).r;
|
||||
yuv[1] = Texel(love_VideoCbChannel, texcoords).r;
|
||||
yuv[2] = Texel(love_VideoCrChannel, texcoords).r;
|
||||
yuv += vec3(-0.0627451017, -0.501960814, -0.501960814);
|
||||
|
||||
vec4 color;
|
||||
color.r = dot(yuv, vec3(1.164, 0.000, 1.596));
|
||||
color.g = dot(yuv, vec3(1.164, -0.391, -0.813));
|
||||
color.b = dot(yuv, vec3(1.164, 2.018, 0.000));
|
||||
color.a = 1.0;
|
||||
|
||||
return gammaCorrectColor(color);
|
||||
}
|
||||
)";
|
||||
|
||||
static const char pixel_main[] = R"(
|
||||
uniform sampler2D MainTex;
|
||||
varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord;
|
||||
varying mediump vec4 VaryingColor;
|
||||
|
||||
vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord);
|
||||
|
||||
void main() {
|
||||
love_initializeBuiltinUniforms();
|
||||
love_PixelColor = effect(VaryingColor, MainTex, VaryingTexCoord.st, love_PixelCoord);
|
||||
}
|
||||
)";
|
||||
|
||||
static const char pixel_main_custom[] = R"(
|
||||
varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord;
|
||||
varying mediump vec4 VaryingColor;
|
||||
|
||||
void effect();
|
||||
|
||||
void main() {
|
||||
love_initializeBuiltinUniforms();
|
||||
effect();
|
||||
}
|
||||
)";
|
||||
|
||||
struct StageInfo
|
||||
{
|
||||
const char *name;
|
||||
const char *header;
|
||||
const char *functions;
|
||||
const char *main;
|
||||
const char *main_custom;
|
||||
};
|
||||
|
||||
static const StageInfo stageInfo[] =
|
||||
{
|
||||
{ "VERTEX", vertex_header, vertex_functions, vertex_main, vertex_main },
|
||||
{ "PIXEL", pixel_header, pixel_functions, pixel_main, pixel_main_custom },
|
||||
};
|
||||
|
||||
static_assert((sizeof(stageInfo) / sizeof(StageInfo)) == ShaderStage::STAGE_MAX_ENUM, "Stages array size must match ShaderStage enum.");
|
||||
|
||||
struct Version
|
||||
{
|
||||
std::string glsl;
|
||||
std::string glsles;
|
||||
};
|
||||
|
||||
// Indexed by Shader::Version
|
||||
static const Version versions[] =
|
||||
{
|
||||
{ "#version 120", "#version 100" },
|
||||
{ "#version 330 core", "#version 300 es" },
|
||||
{ "#version 430 core", "#version 310 es" },
|
||||
};
|
||||
|
||||
static Shader::Language getTargetLanguage(const std::string &src)
|
||||
{
|
||||
std::regex r("^\\s*#pragma language (\\w+)");
|
||||
std::smatch m;
|
||||
std::string langstr = std::regex_search(src, m, r) && m.size() > 1 ? m[1] : std::string("glsl1");
|
||||
Shader::Language lang = Shader::LANGUAGE_MAX_ENUM;
|
||||
Shader::getConstant(langstr.c_str(), lang);
|
||||
return lang;
|
||||
}
|
||||
|
||||
static bool isVertexCode(const std::string &src)
|
||||
{
|
||||
std::regex r("vec4\\s+position\\s*\\(");
|
||||
std::smatch m;
|
||||
return std::regex_search(src, m, r);
|
||||
}
|
||||
|
||||
static bool isPixelCode(const std::string &src, bool &custompixel, bool &mrt)
|
||||
{
|
||||
custompixel = false;
|
||||
mrt = false;
|
||||
std::smatch m;
|
||||
if (std::regex_search(src, m, std::regex("vec4\\s+effect\\s*\\(")))
|
||||
return true;
|
||||
|
||||
if (std::regex_search(src, m, std::regex("void\\s+effect\\s*\\(")))
|
||||
{
|
||||
custompixel = true;
|
||||
if (src.find("love_RenderTargets") != std::string::npos || src.find("love_Canvases") != std::string::npos)
|
||||
mrt = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // glsl
|
||||
|
||||
static_assert(sizeof(Shader::BuiltinUniformData) == sizeof(float) * 4 * 13, "Update the array in wrap_GraphicsShader.lua if this changes.");
|
||||
|
||||
love::Type Shader::type("Shader", &Object::type);
|
||||
@@ -41,6 +431,59 @@ love::Type Shader::type("Shader", &Object::type);
|
||||
Shader *Shader::current = nullptr;
|
||||
Shader *Shader::standardShaders[Shader::STANDARD_MAX_ENUM] = {nullptr};
|
||||
|
||||
Shader::SourceInfo Shader::getSourceInfo(const std::string &src)
|
||||
{
|
||||
SourceInfo info = {};
|
||||
info.language = glsl::getTargetLanguage(src);
|
||||
info.isStage[ShaderStage::STAGE_VERTEX] = glsl::isVertexCode(src);
|
||||
info.isStage[ShaderStage::STAGE_PIXEL] = glsl::isPixelCode(src, info.customPixelFunction, info.usesMRT);
|
||||
return info;
|
||||
}
|
||||
|
||||
std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStage::StageType stage, const std::string &code, const Shader::SourceInfo &info)
|
||||
{
|
||||
if (info.language == Shader::LANGUAGE_MAX_ENUM)
|
||||
throw love::Exception("Invalid shader language");
|
||||
|
||||
const auto &features = gfx->getCapabilities().features;
|
||||
|
||||
if (info.language == LANGUAGE_GLSL3 && !features[Graphics::FEATURE_GLSL3])
|
||||
throw love::Exception("GLSL 3 shaders are not supported on this system.");
|
||||
|
||||
if (info.language == LANGUAGE_GLSL4 && !features[Graphics::FEATURE_GLSL4])
|
||||
throw love::Exception("GLSL 4 shaders are not supported on this system.");
|
||||
|
||||
bool gles = gfx->getRenderer() == Graphics::RENDERER_OPENGLES;
|
||||
bool glsl1on3 = info.language == LANGUAGE_GLSL1 && features[Graphics::FEATURE_GLSL3];
|
||||
|
||||
Language lang = info.language;
|
||||
if (glsl1on3)
|
||||
lang = LANGUAGE_GLSL3;
|
||||
|
||||
glsl::StageInfo stageinfo = glsl::stageInfo[stage];
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
ss << (gles ? glsl::versions[lang].glsles : glsl::versions[lang].glsl) << "\n";
|
||||
ss << "#define " << stageinfo.name << " " << stageinfo.name << "\n";
|
||||
if (glsl1on3)
|
||||
ss << "#define LOVE_GLSL1_ON_GLSL3 1\n";
|
||||
if (isGammaCorrect())
|
||||
ss << "#define LOVE_GAMMA_CORRECT 1\n";
|
||||
if (info.usesMRT)
|
||||
ss << "#define LOVE_MULTI_RENDER_TARGETS 1";
|
||||
ss << glsl::global_syntax;
|
||||
ss << stageinfo.header;
|
||||
ss << glsl::global_uniforms;
|
||||
ss << glsl::global_functions;
|
||||
ss << stageinfo.functions;
|
||||
ss << (info.customPixelFunction ? stageinfo.main_custom : stageinfo.main);
|
||||
ss << ((!gles && (lang == Shader::LANGUAGE_GLSL1 || glsl1on3)) ? "#line 0\n" : "#line 1\n");
|
||||
ss << code;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
Shader::Shader(ShaderStage *vertex, ShaderStage *pixel)
|
||||
: stages()
|
||||
{
|
||||
@@ -157,6 +600,74 @@ void Shader::deinitialize()
|
||||
glslang::FinalizeProcess();
|
||||
}
|
||||
|
||||
static const std::string defaultVertex = R"(
|
||||
vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition)
|
||||
{
|
||||
return clipSpaceFromLocal * localPosition;
|
||||
}
|
||||
)";
|
||||
|
||||
static const std::string defaultStandardPixel = R"(
|
||||
vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord)
|
||||
{
|
||||
return Texel(tex, texcoord) * vcolor;
|
||||
}
|
||||
)";
|
||||
|
||||
static const std::string defaultVideoPixel = R"(
|
||||
void effect()
|
||||
{
|
||||
love_PixelColor = VideoTexel(VaryingTexCoord.xy) * VaryingColor;
|
||||
}
|
||||
)";
|
||||
|
||||
static const std::string defaultArrayPixel = R"(
|
||||
uniform ArrayImage MainTex;
|
||||
void effect()
|
||||
{
|
||||
love_PixelColor = Texel(MainTex, VaryingTexCoord.xyz) * VaryingColor;
|
||||
}
|
||||
)";
|
||||
|
||||
const std::string &Shader::getDefaultCode(StandardShader shader, ShaderStage::StageType stage)
|
||||
{
|
||||
if (stage == ShaderStage::STAGE_VERTEX)
|
||||
return defaultVertex;
|
||||
|
||||
static std::string nocode = "";
|
||||
|
||||
switch (shader)
|
||||
{
|
||||
case STANDARD_DEFAULT: return defaultStandardPixel;
|
||||
case STANDARD_VIDEO: return defaultVideoPixel;
|
||||
case STANDARD_ARRAY: return defaultArrayPixel;
|
||||
case STANDARD_MAX_ENUM: return nocode;
|
||||
}
|
||||
|
||||
return nocode;
|
||||
}
|
||||
|
||||
static StringMap<Shader::Language, Shader::LANGUAGE_MAX_ENUM>::Entry languageEntries[] =
|
||||
{
|
||||
{ "glsl1", Shader::LANGUAGE_GLSL1 },
|
||||
{ "glsl3", Shader::LANGUAGE_GLSL3 },
|
||||
{ "glsl4", Shader::LANGUAGE_GLSL4 },
|
||||
};
|
||||
|
||||
static StringMap<Shader::Language, Shader::LANGUAGE_MAX_ENUM> languages(languageEntries, sizeof(languageEntries));
|
||||
|
||||
static StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM>::Entry builtinNameEntries[] =
|
||||
{
|
||||
{ "MainTex", Shader::BUILTIN_TEXTURE_MAIN },
|
||||
{ "love_VideoYChannel", Shader::BUILTIN_TEXTURE_VIDEO_Y },
|
||||
{ "love_VideoCbChannel", Shader::BUILTIN_TEXTURE_VIDEO_CB },
|
||||
{ "love_VideoCrChannel", Shader::BUILTIN_TEXTURE_VIDEO_CR },
|
||||
{ "love_UniformsPerDraw", Shader::BUILTIN_UNIFORMS_PER_DRAW },
|
||||
{ "love_PointSize", Shader::BUILTIN_POINT_SIZE },
|
||||
};
|
||||
|
||||
static StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM> builtinNames(builtinNameEntries, sizeof(builtinNameEntries));
|
||||
|
||||
bool Shader::getConstant(const char *in, Language &out)
|
||||
{
|
||||
return languages.find(in, out);
|
||||
@@ -177,27 +688,5 @@ bool Shader::getConstant(BuiltinUniform in, const char *&out)
|
||||
return builtinNames.find(in, out);
|
||||
}
|
||||
|
||||
StringMap<Shader::Language, Shader::LANGUAGE_MAX_ENUM>::Entry Shader::languageEntries[] =
|
||||
{
|
||||
{ "glsl1", LANGUAGE_GLSL1 },
|
||||
{ "essl1", LANGUAGE_ESSL1 },
|
||||
{ "glsl3", LANGUAGE_GLSL3 },
|
||||
{ "essl3", LANGUAGE_ESSL3 },
|
||||
};
|
||||
|
||||
StringMap<Shader::Language, Shader::LANGUAGE_MAX_ENUM> Shader::languages(Shader::languageEntries, sizeof(Shader::languageEntries));
|
||||
|
||||
StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM>::Entry Shader::builtinNameEntries[] =
|
||||
{
|
||||
{ "MainTex", BUILTIN_TEXTURE_MAIN },
|
||||
{ "love_VideoYChannel", BUILTIN_TEXTURE_VIDEO_Y },
|
||||
{ "love_VideoCbChannel", BUILTIN_TEXTURE_VIDEO_CB },
|
||||
{ "love_VideoCrChannel", BUILTIN_TEXTURE_VIDEO_CR },
|
||||
{ "love_UniformsPerDraw", BUILTIN_UNIFORMS_PER_DRAW },
|
||||
{ "love_PointSize", BUILTIN_POINT_SIZE },
|
||||
};
|
||||
|
||||
StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM> Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries));
|
||||
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -33,17 +33,13 @@
|
||||
#include <vector>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace glslang
|
||||
{
|
||||
class TShader;
|
||||
}
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
class Graphics;
|
||||
class Buffer;
|
||||
|
||||
// A GLSL shader
|
||||
class Shader : public Object, public Resource
|
||||
@@ -55,9 +51,8 @@ public:
|
||||
enum Language
|
||||
{
|
||||
LANGUAGE_GLSL1,
|
||||
LANGUAGE_ESSL1,
|
||||
LANGUAGE_GLSL3,
|
||||
LANGUAGE_ESSL3,
|
||||
LANGUAGE_GLSL4,
|
||||
LANGUAGE_MAX_ENUM
|
||||
};
|
||||
|
||||
@@ -82,6 +77,7 @@ public:
|
||||
UNIFORM_UINT,
|
||||
UNIFORM_BOOL,
|
||||
UNIFORM_SAMPLER,
|
||||
UNIFORM_TEXELBUFFER,
|
||||
UNIFORM_UNKNOWN,
|
||||
UNIFORM_MAX_ENUM
|
||||
};
|
||||
@@ -94,6 +90,14 @@ public:
|
||||
STANDARD_MAX_ENUM
|
||||
};
|
||||
|
||||
struct SourceInfo
|
||||
{
|
||||
Language language;
|
||||
bool isStage[ShaderStage::STAGE_MAX_ENUM];
|
||||
bool customPixelFunction;
|
||||
bool usesMRT;
|
||||
};
|
||||
|
||||
struct MatrixSize
|
||||
{
|
||||
short columns;
|
||||
@@ -113,6 +117,7 @@ public:
|
||||
|
||||
UniformType baseType;
|
||||
TextureType textureType;
|
||||
DataBaseType texelBufferType;
|
||||
bool isDepthSampler;
|
||||
std::string name;
|
||||
|
||||
@@ -126,7 +131,11 @@ public:
|
||||
|
||||
size_t dataSize;
|
||||
|
||||
Texture **textures;
|
||||
union
|
||||
{
|
||||
Texture **textures;
|
||||
Buffer **buffers;
|
||||
};
|
||||
};
|
||||
|
||||
// The members in here must respect uniform buffer alignment/padding rules.
|
||||
@@ -176,6 +185,7 @@ public:
|
||||
virtual void updateUniform(const UniformInfo *info, int count) = 0;
|
||||
|
||||
virtual void sendTextures(const UniformInfo *info, Texture **textures, int count) = 0;
|
||||
virtual void sendBuffers(const UniformInfo *info, Buffer **buffers, int count) = 0;
|
||||
|
||||
/**
|
||||
* Gets whether a uniform with the specified name exists and is actively
|
||||
@@ -192,11 +202,16 @@ public:
|
||||
void checkMainTextureType(TextureType textype, bool isDepthSampler) const;
|
||||
void checkMainTexture(Texture *texture) const;
|
||||
|
||||
static SourceInfo getSourceInfo(const std::string &src);
|
||||
static std::string createShaderStageCode(Graphics *gfx, ShaderStage::StageType stage, const std::string &code, const SourceInfo &info);
|
||||
|
||||
static bool validate(ShaderStage *vertex, ShaderStage *pixel, std::string &err);
|
||||
|
||||
static bool initialize();
|
||||
static void deinitialize();
|
||||
|
||||
static const std::string &getDefaultCode(StandardShader shader, ShaderStage::StageType stage);
|
||||
|
||||
static bool getConstant(const char *in, Language &out);
|
||||
static bool getConstant(Language in, const char *&out);
|
||||
|
||||
@@ -207,15 +222,6 @@ protected:
|
||||
|
||||
StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM];
|
||||
|
||||
private:
|
||||
|
||||
static StringMap<Language, LANGUAGE_MAX_ENUM>::Entry languageEntries[];
|
||||
static StringMap<Language, LANGUAGE_MAX_ENUM> languages;
|
||||
|
||||
// Names for the built-in uniform variables.
|
||||
static StringMap<BuiltinUniform, BUILTIN_MAX_ENUM>::Entry builtinNameEntries[];
|
||||
static StringMap<BuiltinUniform, BUILTIN_MAX_ENUM> builtinNames;
|
||||
|
||||
}; // Shader
|
||||
|
||||
} // graphics
|
||||
|
||||
@@ -205,6 +205,13 @@ bool ShaderStage::getConstant(StageType in, const char *&out)
|
||||
return stageNames.find(in, out);
|
||||
}
|
||||
|
||||
const char *ShaderStage::getConstant(StageType in)
|
||||
{
|
||||
const char *name = nullptr;
|
||||
getConstant(in, name);
|
||||
return name;
|
||||
}
|
||||
|
||||
StringMap<ShaderStage::StageType, ShaderStage::STAGE_MAX_ENUM>::Entry ShaderStage::stageNameEntries[] =
|
||||
{
|
||||
{ "vertex", STAGE_VERTEX },
|
||||
|
||||
@@ -43,6 +43,7 @@ class ShaderStage : public love::Object
|
||||
{
|
||||
public:
|
||||
|
||||
// Order is used for stages array in ShaderStage.cpp
|
||||
enum StageType
|
||||
{
|
||||
STAGE_VERTEX,
|
||||
@@ -62,6 +63,7 @@ public:
|
||||
|
||||
static bool getConstant(const char *in, StageType &out);
|
||||
static bool getConstant(StageType in, const char *&out);
|
||||
static const char *getConstant(StageType in);
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace graphics
|
||||
|
||||
love::Type SpriteBatch::type("SpriteBatch", &Drawable::type);
|
||||
|
||||
SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage)
|
||||
SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, BufferUsage usage)
|
||||
: texture(texture)
|
||||
, size(size)
|
||||
, next(0)
|
||||
@@ -57,14 +57,16 @@ SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usag
|
||||
throw love::Exception("A texture must be used when creating a SpriteBatch.");
|
||||
|
||||
if (texture->getTextureType() == TEXTURE_2D_ARRAY)
|
||||
vertex_format = vertex::CommonFormat::XYf_STPf_RGBAub;
|
||||
vertex_format = CommonFormat::XYf_STPf_RGBAub;
|
||||
else
|
||||
vertex_format = vertex::CommonFormat::XYf_STf_RGBAub;
|
||||
vertex_format = CommonFormat::XYf_STf_RGBAub;
|
||||
|
||||
vertex_stride = vertex::getFormatStride(vertex_format);
|
||||
vertex_stride = getFormatStride(vertex_format);
|
||||
|
||||
size_t vertex_size = vertex_stride * 4 * size;
|
||||
array_buf = gfx->newBuffer(vertex_size, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY, usage);
|
||||
auto decl = Buffer::getCommonFormatDeclaration(vertex_format);
|
||||
array_buf = gfx->newBuffer(settings, decl, nullptr, vertex_size, 0);
|
||||
}
|
||||
|
||||
SpriteBatch::~SpriteBatch()
|
||||
@@ -79,8 +81,6 @@ int SpriteBatch::add(const Matrix4 &m, int index /*= -1*/)
|
||||
|
||||
int SpriteBatch::add(Quad *quad, const Matrix4 &m, int index /*= -1*/)
|
||||
{
|
||||
using namespace vertex;
|
||||
|
||||
if (vertex_format == CommonFormat::XYf_STPf_RGBAub)
|
||||
return addLayer(quad->getLayer(), quad, m, index);
|
||||
|
||||
@@ -122,8 +122,6 @@ int SpriteBatch::addLayer(int layer, const Matrix4 &m, int index)
|
||||
|
||||
int SpriteBatch::addLayer(int layer, Quad *quad, const Matrix4 &m, int index)
|
||||
{
|
||||
using namespace vertex;
|
||||
|
||||
if (vertex_format != CommonFormat::XYf_STPf_RGBAub)
|
||||
throw love::Exception("addLayer can only be called on a SpriteBatch that uses an Array Texture!");
|
||||
|
||||
@@ -222,7 +220,9 @@ void SpriteBatch::setBufferSize(int newsize)
|
||||
try
|
||||
{
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
new_array_buf = gfx->newBuffer(vertex_size, nullptr, array_buf->getType(), array_buf->getUsage(), array_buf->getMapFlags());
|
||||
Buffer::Settings settings(array_buf->getTypeFlags(), array_buf->getMapFlags(), array_buf->getUsage());
|
||||
auto decl = Buffer::getCommonFormatDeclaration(vertex_format);
|
||||
new_array_buf = gfx->newBuffer(settings, decl, nullptr, vertex_size, 0);
|
||||
|
||||
// Copy as much of the old data into the new GLBuffer as can fit.
|
||||
size_t copy_size = vertex_stride * 4 * new_next;
|
||||
@@ -248,24 +248,27 @@ int SpriteBatch::getBufferSize() const
|
||||
return size;
|
||||
}
|
||||
|
||||
void SpriteBatch::attachAttribute(const std::string &name, Mesh *mesh)
|
||||
void SpriteBatch::attachAttribute(const std::string &name, Buffer *buffer)
|
||||
{
|
||||
if ((buffer->getTypeFlags() & Buffer::TYPEFLAG_VERTEX) == 0)
|
||||
throw love::Exception("GraphicsBuffer must be created with vertex buffer support to be used as a SpriteBatch vertex attribute.");
|
||||
|
||||
AttachedAttribute oldattrib = {};
|
||||
AttachedAttribute newattrib = {};
|
||||
|
||||
if (mesh->getVertexCount() < (size_t) next * 4)
|
||||
throw love::Exception("Mesh has too few vertices to be attached to this SpriteBatch (at least %d vertices are required)", next*4);
|
||||
if (buffer->getArrayLength() < (size_t) next * 4)
|
||||
throw love::Exception("Buffer has too few vertices to be attached to this SpriteBatch (at least %d vertices are required)", next*4);
|
||||
|
||||
auto it = attached_attributes.find(name);
|
||||
if (it != attached_attributes.end())
|
||||
oldattrib = it->second;
|
||||
|
||||
newattrib.index = mesh->getAttributeIndex(name);
|
||||
newattrib.index = buffer->getDataMemberIndex(name);
|
||||
|
||||
if (newattrib.index < 0)
|
||||
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str());
|
||||
throw love::Exception("The specified Buffer does not have a vertex attribute named '%s'", name.c_str());
|
||||
|
||||
newattrib.mesh = mesh;
|
||||
newattrib.buffer = buffer;
|
||||
|
||||
attached_attributes[name] = newattrib;
|
||||
}
|
||||
@@ -296,8 +299,6 @@ bool SpriteBatch::getDrawRange(int &start, int &count) const
|
||||
|
||||
void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
{
|
||||
using namespace vertex;
|
||||
|
||||
if (next == 0)
|
||||
return;
|
||||
|
||||
@@ -321,7 +322,7 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
// Make sure the buffer isn't mapped when we draw (sends data to GPU if needed.)
|
||||
array_buf->unmap();
|
||||
|
||||
Attributes attributes;
|
||||
VertexAttributes attributes;
|
||||
BufferBindings buffers;
|
||||
|
||||
{
|
||||
@@ -333,19 +334,19 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
|
||||
for (const auto &it : attached_attributes)
|
||||
{
|
||||
Mesh *mesh = it.second.mesh.get();
|
||||
Buffer *buffer = it.second.buffer.get();
|
||||
|
||||
// We have to do this check here as wll because setBufferSize can be
|
||||
// called after attachAttribute.
|
||||
if (mesh->getVertexCount() < (size_t) next * 4)
|
||||
throw love::Exception("Mesh with attribute '%s' attached to this SpriteBatch has too few vertices", it.first.c_str());
|
||||
if (buffer->getArrayLength() < (size_t) next * 4)
|
||||
throw love::Exception("Buffer with attribute '%s' attached to this SpriteBatch has too few vertices", it.first.c_str());
|
||||
|
||||
int attributeindex = -1;
|
||||
|
||||
// If the attribute is one of the LOVE-defined ones, use the constant
|
||||
// attribute index for it, otherwise query the index from the shader.
|
||||
BuiltinVertexAttribute builtinattrib;
|
||||
if (vertex::getConstant(it.first.c_str(), builtinattrib))
|
||||
if (getConstant(it.first.c_str(), builtinattrib))
|
||||
attributeindex = (int) builtinattrib;
|
||||
else if (Shader::current)
|
||||
attributeindex = Shader::current->getVertexAttributeIndex(it.first);
|
||||
@@ -353,19 +354,18 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
if (attributeindex >= 0)
|
||||
{
|
||||
// Make sure the buffer isn't mapped (sends data to GPU if needed.)
|
||||
mesh->vertexBuffer->unmap();
|
||||
buffer->unmap();
|
||||
|
||||
const auto &formats = mesh->getVertexFormat();
|
||||
const auto &format = formats[it.second.index];
|
||||
const auto &member = buffer->getDataMember(it.second.index);
|
||||
|
||||
uint16 offset = (uint16) mesh->getAttributeOffset(it.second.index);
|
||||
uint16 stride = (uint16) mesh->getVertexStride();
|
||||
uint16 offset = (uint16) buffer->getMemberOffset(it.second.index);
|
||||
uint16 stride = (uint16) buffer->getArrayStride();
|
||||
|
||||
attributes.set(attributeindex, format.type, (uint8) format.components, offset, activebuffers);
|
||||
attributes.set(attributeindex, member.decl.format, offset, activebuffers);
|
||||
attributes.setBufferLayout(activebuffers, stride);
|
||||
|
||||
// TODO: We should reuse buffer bindings with the same buffer+stride+step.
|
||||
buffers.set(activebuffers, mesh->vertexBuffer, 0);
|
||||
buffers.set(activebuffers, buffer, 0);
|
||||
activebuffers++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
|
||||
static love::Type type;
|
||||
|
||||
SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage);
|
||||
SpriteBatch(Graphics *gfx, Texture *texture, int size, BufferUsage usage);
|
||||
virtual ~SpriteBatch();
|
||||
|
||||
int add(const Matrix4 &m, int index = -1);
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
* Attaches a specific vertex attribute from a Mesh to this SpriteBatch.
|
||||
* The vertex attribute will be used when drawing the SpriteBatch.
|
||||
**/
|
||||
void attachAttribute(const std::string &name, Mesh *mesh);
|
||||
void attachAttribute(const std::string &name, Buffer *buffer);
|
||||
|
||||
void setDrawRange(int start, int count);
|
||||
void setDrawRange();
|
||||
@@ -106,7 +106,7 @@ private:
|
||||
|
||||
struct AttachedAttribute
|
||||
{
|
||||
StrongRef<Mesh> mesh;
|
||||
StrongRef<Buffer> buffer;
|
||||
int index;
|
||||
};
|
||||
|
||||
@@ -128,7 +128,7 @@ private:
|
||||
Color32 color;
|
||||
Colorf colorf;
|
||||
|
||||
vertex::CommonFormat vertex_format;
|
||||
CommonFormat vertex_format;
|
||||
size_t vertex_stride;
|
||||
|
||||
love::graphics::Buffer *array_buf;
|
||||
|
||||
@@ -42,7 +42,8 @@ Text::Text(Font *font, const std::vector<Font::ColoredString> &text)
|
||||
|
||||
Text::~Text()
|
||||
{
|
||||
delete vertex_buffer;
|
||||
if (vertex_buffer)
|
||||
vertex_buffer->release();
|
||||
}
|
||||
|
||||
void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t vertoffset)
|
||||
@@ -60,12 +61,14 @@ void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t
|
||||
newsize = std::max(size_t(vertex_buffer->getSize() * 1.5), newsize);
|
||||
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
Buffer *new_buffer = gfx->newBuffer(newsize, nullptr, BUFFER_VERTEX, vertex::USAGE_DYNAMIC, 0);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, 0, BUFFERUSAGE_DYNAMIC);
|
||||
auto decl = Buffer::getCommonFormatDeclaration(Font::vertexFormat);
|
||||
Buffer *new_buffer = gfx->newBuffer(settings, decl, nullptr, newsize, 0);
|
||||
|
||||
if (vertex_buffer != nullptr)
|
||||
vertex_buffer->copyTo(0, vertex_buffer->getSize(), new_buffer, 0);
|
||||
|
||||
delete vertex_buffer;
|
||||
vertex_buffer->release();
|
||||
vertex_buffer = new_buffer;
|
||||
|
||||
vertexBuffers.set(0, vertex_buffer, 0);
|
||||
|
||||
@@ -85,8 +85,8 @@ private:
|
||||
|
||||
StrongRef<Font> font;
|
||||
|
||||
vertex::Attributes vertexAttributes;
|
||||
vertex::BufferBindings vertexBuffers;
|
||||
VertexAttributes vertexAttributes;
|
||||
BufferBindings vertexBuffers;
|
||||
|
||||
Buffer *vertex_buffer;
|
||||
|
||||
|
||||
@@ -192,6 +192,8 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
|
||||
love::image::ImageDataBase *slice = slices->get(0, 0);
|
||||
|
||||
format = slice->getFormat();
|
||||
if (sRGB)
|
||||
format = getSRGBPixelFormat(format);
|
||||
|
||||
pixelWidth = slice->getWidth();
|
||||
pixelHeight = slice->getHeight();
|
||||
@@ -305,8 +307,6 @@ void Texture::draw(Graphics *gfx, const Matrix4 &m)
|
||||
|
||||
void Texture::draw(Graphics *gfx, Quad *q, const Matrix4 &localTransform)
|
||||
{
|
||||
using namespace vertex;
|
||||
|
||||
if (!readable)
|
||||
throw love::Exception("Textures with non-readable formats cannot be drawn.");
|
||||
|
||||
@@ -323,9 +323,9 @@ void Texture::draw(Graphics *gfx, Quad *q, const Matrix4 &localTransform)
|
||||
bool is2D = tm.isAffine2DTransform();
|
||||
|
||||
Graphics::BatchedDrawCommand cmd;
|
||||
cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
|
||||
cmd.formats[0] = getSinglePositionFormat(is2D);
|
||||
cmd.formats[1] = CommonFormat::STf_RGBAub;
|
||||
cmd.indexMode = TriangleIndexMode::QUADS;
|
||||
cmd.indexMode = TRIANGLEINDEX_QUADS;
|
||||
cmd.vertexCount = 4;
|
||||
cmd.texture = this;
|
||||
|
||||
@@ -339,7 +339,7 @@ void Texture::draw(Graphics *gfx, Quad *q, const Matrix4 &localTransform)
|
||||
t.transformXY0((Vector3 *) data.stream[0], q->getVertexPositions(), 4);
|
||||
|
||||
const Vector2 *texcoords = q->getVertexTexCoords();
|
||||
vertex::STf_RGBAub *vertexdata = (vertex::STf_RGBAub *) data.stream[1];
|
||||
STf_RGBAub *vertexdata = (STf_RGBAub *) data.stream[1];
|
||||
|
||||
Color32 c = toColor32(gfx->getColor());
|
||||
|
||||
@@ -358,8 +358,6 @@ void Texture::drawLayer(Graphics *gfx, int layer, const Matrix4 &m)
|
||||
|
||||
void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m)
|
||||
{
|
||||
using namespace vertex;
|
||||
|
||||
if (!readable)
|
||||
throw love::Exception("Textures with non-readable formats cannot be drawn.");
|
||||
|
||||
@@ -380,9 +378,9 @@ void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m)
|
||||
Matrix4 t(tm, m);
|
||||
|
||||
Graphics::BatchedDrawCommand cmd;
|
||||
cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
|
||||
cmd.formats[0] = getSinglePositionFormat(is2D);
|
||||
cmd.formats[1] = CommonFormat::STPf_RGBAub;
|
||||
cmd.indexMode = TriangleIndexMode::QUADS;
|
||||
cmd.indexMode = TRIANGLEINDEX_QUADS;
|
||||
cmd.vertexCount = 4;
|
||||
cmd.texture = this;
|
||||
cmd.standardShaderType = Shader::STANDARD_ARRAY;
|
||||
@@ -395,7 +393,7 @@ void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m)
|
||||
t.transformXY0((Vector3 *) data.stream[0], q->getVertexPositions(), 4);
|
||||
|
||||
const Vector2 *texcoords = q->getVertexTexCoords();
|
||||
vertex::STPf_RGBAub *vertexdata = (vertex::STPf_RGBAub *) data.stream[1];
|
||||
STPf_RGBAub *vertexdata = (STPf_RGBAub *) data.stream[1];
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
|
||||
@@ -121,9 +121,9 @@ void Video::draw(Graphics *gfx, const Matrix4 &m)
|
||||
Matrix4 t(tm, m);
|
||||
|
||||
Graphics::BatchedDrawCommand cmd;
|
||||
cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
|
||||
cmd.formats[1] = vertex::CommonFormat::STf_RGBAub;
|
||||
cmd.indexMode = vertex::TriangleIndexMode::QUADS;
|
||||
cmd.formats[0] = getSinglePositionFormat(is2D);
|
||||
cmd.formats[1] = CommonFormat::STf_RGBAub;
|
||||
cmd.indexMode = TRIANGLEINDEX_QUADS;
|
||||
cmd.vertexCount = 4;
|
||||
cmd.standardShaderType = Shader::STANDARD_VIDEO;
|
||||
|
||||
@@ -134,7 +134,7 @@ void Video::draw(Graphics *gfx, const Matrix4 &m)
|
||||
else
|
||||
t.transformXY0((Vector3 *) data.stream[0], vertices, 4);
|
||||
|
||||
vertex::STf_RGBAub *verts = (vertex::STf_RGBAub *) data.stream[1];
|
||||
STf_RGBAub *verts = (STf_RGBAub *) data.stream[1];
|
||||
|
||||
Color32 c = toColor32(gfx->getColor());
|
||||
|
||||
|
||||
@@ -35,18 +35,54 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
Buffer::Buffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags)
|
||||
: love::graphics::Buffer(size, type, usage, mapflags)
|
||||
, vbo(0)
|
||||
, memory_map(nullptr)
|
||||
, modified_offset(0)
|
||||
, modified_size(0)
|
||||
static GLenum getGLFormat(DataFormat format)
|
||||
{
|
||||
target = OpenGL::getGLBufferType(type);
|
||||
switch (format)
|
||||
{
|
||||
case DATAFORMAT_FLOAT: return GL_R32F;
|
||||
case DATAFORMAT_FLOAT_VEC2: return GL_RG32F;
|
||||
case DATAFORMAT_FLOAT_VEC3: return GL_RGB32F;
|
||||
case DATAFORMAT_FLOAT_VEC4: return GL_RGBA32F;
|
||||
case DATAFORMAT_INT32: return GL_R32I;
|
||||
case DATAFORMAT_INT32_VEC2: return GL_RG32I;
|
||||
case DATAFORMAT_INT32_VEC3: return GL_RGB32I;
|
||||
case DATAFORMAT_INT32_VEC4: return GL_RGBA32I;
|
||||
case DATAFORMAT_UINT32: return GL_R32UI;
|
||||
case DATAFORMAT_UINT32_VEC2: return GL_RG32UI;
|
||||
case DATAFORMAT_UINT32_VEC3: return GL_RGB32UI;
|
||||
case DATAFORMAT_UINT32_VEC4: return GL_RGBA32UI;
|
||||
case DATAFORMAT_UNORM8_VEC4: return GL_RGBA8;
|
||||
case DATAFORMAT_INT8_VEC4: return GL_RGBA8I;
|
||||
case DATAFORMAT_UINT8_VEC4: return GL_RGBA8UI;
|
||||
case DATAFORMAT_UNORM16_VEC2: return GL_RG16;
|
||||
case DATAFORMAT_UNORM16_VEC4: return GL_RGBA16;
|
||||
case DATAFORMAT_INT16_VEC2: return GL_RG16I;
|
||||
case DATAFORMAT_INT16_VEC4: return GL_RGBA16I;
|
||||
case DATAFORMAT_UINT16: return GL_R16UI;
|
||||
case DATAFORMAT_UINT16_VEC2: return GL_RG16UI;
|
||||
case DATAFORMAT_UINT16_VEC4: return GL_RGBA16UI;
|
||||
default: return GL_ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const std::vector<DataDeclaration> &format, const void *data, size_t size, size_t arraylength)
|
||||
: love::graphics::Buffer(gfx, settings, format, size, arraylength)
|
||||
{
|
||||
size = getSize();
|
||||
arraylength = getArrayLength();
|
||||
|
||||
if (typeFlags & TYPEFLAG_TEXEL)
|
||||
mapType = BUFFERTYPE_TEXEL;
|
||||
else if (typeFlags & TYPEFLAG_VERTEX)
|
||||
mapType = BUFFERTYPE_VERTEX;
|
||||
else if (typeFlags & TYPEFLAG_INDEX)
|
||||
mapType = BUFFERTYPE_INDEX;
|
||||
|
||||
target = OpenGL::getGLBufferType(mapType);
|
||||
|
||||
try
|
||||
{
|
||||
memory_map = new char[size];
|
||||
memoryMap = new char[size];
|
||||
}
|
||||
catch (std::bad_alloc &)
|
||||
{
|
||||
@@ -54,34 +90,78 @@ Buffer::Buffer(size_t size, const void *data, BufferType type, vertex::Usage usa
|
||||
}
|
||||
|
||||
if (data != nullptr)
|
||||
memcpy(memory_map, data, size);
|
||||
memcpy(memoryMap, data, size);
|
||||
|
||||
if (!load(data != nullptr))
|
||||
{
|
||||
delete[] memory_map;
|
||||
throw love::Exception("Could not load vertex buffer (out of VRAM?)");
|
||||
unloadVolatile();
|
||||
delete[] memoryMap;
|
||||
throw love::Exception("Could not create buffer (out of VRAM?)");
|
||||
}
|
||||
}
|
||||
|
||||
Buffer::~Buffer()
|
||||
{
|
||||
if (vbo != 0)
|
||||
unload();
|
||||
unloadVolatile();
|
||||
delete[] memoryMap;
|
||||
}
|
||||
|
||||
delete[] memory_map;
|
||||
bool Buffer::loadVolatile()
|
||||
{
|
||||
if (buffer != 0)
|
||||
return true;
|
||||
|
||||
return load(true);
|
||||
}
|
||||
|
||||
void Buffer::unloadVolatile()
|
||||
{
|
||||
mapped = false;
|
||||
if (buffer != 0)
|
||||
gl.deleteBuffer(buffer);
|
||||
buffer = 0;
|
||||
if (texture != 0)
|
||||
gl.deleteTexture(texture);
|
||||
texture = 0;
|
||||
}
|
||||
|
||||
bool Buffer::load(bool restore)
|
||||
{
|
||||
while (glGetError() != GL_NO_ERROR)
|
||||
/* Clear the error buffer. */;
|
||||
|
||||
glGenBuffers(1, &buffer);
|
||||
gl.bindBuffer(mapType, buffer);
|
||||
|
||||
// Copy the old buffer only if 'restore' was requested.
|
||||
const GLvoid *src = restore ? memoryMap : nullptr;
|
||||
|
||||
// Note that if 'src' is '0', no data will be copied.
|
||||
glBufferData(target, (GLsizeiptr) getSize(), src, OpenGL::getGLBufferUsage(getUsage()));
|
||||
|
||||
if (getTypeFlags() & TYPEFLAG_TEXEL)
|
||||
{
|
||||
glGenTextures(1, &texture);
|
||||
gl.bindBufferTextureToUnit(texture, 0, false, true);
|
||||
|
||||
glTexBuffer(target, getGLFormat(getDataMember(0).decl.format), buffer);
|
||||
}
|
||||
|
||||
return (glGetError() == GL_NO_ERROR);
|
||||
}
|
||||
|
||||
void *Buffer::map()
|
||||
{
|
||||
if (is_mapped)
|
||||
return memory_map;
|
||||
if (mapped)
|
||||
return memoryMap;
|
||||
|
||||
is_mapped = true;
|
||||
mapped = true;
|
||||
|
||||
modified_offset = 0;
|
||||
modified_size = 0;
|
||||
modifiedOffset = 0;
|
||||
modifiedSize = 0;
|
||||
isMappedDataModified = false;
|
||||
|
||||
return memory_map;
|
||||
return memoryMap;
|
||||
}
|
||||
|
||||
void Buffer::unmapStatic(size_t offset, size_t size)
|
||||
@@ -90,8 +170,8 @@ void Buffer::unmapStatic(size_t offset, size_t size)
|
||||
return;
|
||||
|
||||
// Upload the mapped data to the buffer.
|
||||
gl.bindBuffer(type, vbo);
|
||||
glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, memory_map + offset);
|
||||
gl.bindBuffer(mapType, buffer);
|
||||
glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, memoryMap + offset);
|
||||
}
|
||||
|
||||
void Buffer::unmapStream()
|
||||
@@ -100,133 +180,105 @@ void Buffer::unmapStream()
|
||||
|
||||
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
|
||||
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
|
||||
gl.bindBuffer(type, vbo);
|
||||
gl.bindBuffer(mapType, buffer);
|
||||
glBufferData(target, (GLsizeiptr) getSize(), nullptr, glusage);
|
||||
|
||||
#if LOVE_WINDOWS
|
||||
// TODO: Verify that this codepath is a useful optimization.
|
||||
if (gl.getVendor() == OpenGL::VENDOR_INTEL)
|
||||
glBufferData(target, (GLsizeiptr) getSize(), memory_map, glusage);
|
||||
glBufferData(target, (GLsizeiptr) getSize(), memoryMap, glusage);
|
||||
else
|
||||
#endif
|
||||
glBufferSubData(target, 0, (GLsizeiptr) getSize(), memory_map);
|
||||
glBufferSubData(target, 0, (GLsizeiptr) getSize(), memoryMap);
|
||||
}
|
||||
|
||||
void Buffer::unmap()
|
||||
{
|
||||
if (!is_mapped)
|
||||
if (!mapped)
|
||||
return;
|
||||
|
||||
if ((map_flags & MAP_EXPLICIT_RANGE_MODIFY) != 0)
|
||||
mapped = false;
|
||||
|
||||
if ((mapFlags & MAP_EXPLICIT_RANGE_MODIFY) != 0)
|
||||
{
|
||||
modified_offset = std::min(modified_offset, getSize() - 1);
|
||||
modified_size = std::min(modified_size, getSize() - modified_offset);
|
||||
if (!isMappedDataModified)
|
||||
return;
|
||||
|
||||
modifiedOffset = std::min(modifiedOffset, getSize() - 1);
|
||||
modifiedSize = std::min(modifiedSize, getSize() - modifiedOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
modified_offset = 0;
|
||||
modified_size = getSize();
|
||||
modifiedOffset = 0;
|
||||
modifiedSize = getSize();
|
||||
}
|
||||
|
||||
if (modified_size > 0)
|
||||
if (modifiedSize > 0)
|
||||
{
|
||||
switch (getUsage())
|
||||
{
|
||||
case vertex::USAGE_STATIC:
|
||||
unmapStatic(modified_offset, modified_size);
|
||||
case BUFFERUSAGE_STATIC:
|
||||
unmapStatic(modifiedOffset, modifiedSize);
|
||||
break;
|
||||
case vertex::USAGE_STREAM:
|
||||
case BUFFERUSAGE_STREAM:
|
||||
unmapStream();
|
||||
break;
|
||||
case vertex::USAGE_DYNAMIC:
|
||||
case BUFFERUSAGE_DYNAMIC:
|
||||
default:
|
||||
// It's probably more efficient to treat it like a streaming buffer if
|
||||
// at least a third of its contents have been modified during the map().
|
||||
if (modified_size >= getSize() / 3)
|
||||
if (modifiedSize >= getSize() / 3)
|
||||
unmapStream();
|
||||
else
|
||||
unmapStatic(modified_offset, modified_size);
|
||||
unmapStatic(modifiedOffset, modifiedSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
modified_offset = 0;
|
||||
modified_size = 0;
|
||||
|
||||
is_mapped = false;
|
||||
modifiedOffset = 0;
|
||||
modifiedSize = 0;
|
||||
}
|
||||
|
||||
void Buffer::setMappedRangeModified(size_t offset, size_t modifiedsize)
|
||||
{
|
||||
if (!is_mapped || !(map_flags & MAP_EXPLICIT_RANGE_MODIFY))
|
||||
if (!mapped || !(mapFlags & MAP_EXPLICIT_RANGE_MODIFY))
|
||||
return;
|
||||
|
||||
if (!isMappedDataModified)
|
||||
{
|
||||
modifiedOffset = offset;
|
||||
modifiedSize = modifiedsize;
|
||||
isMappedDataModified = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// We're being conservative right now by internally marking the whole range
|
||||
// from the start of section a to the end of section b as modified if both
|
||||
// a and b are marked as modified.
|
||||
|
||||
size_t old_range_end = modified_offset + modified_size;
|
||||
modified_offset = std::min(modified_offset, offset);
|
||||
size_t oldrangeend = modifiedOffset + modifiedSize;
|
||||
modifiedOffset = std::min(modifiedOffset, offset);
|
||||
|
||||
size_t new_range_end = std::max(offset + modifiedsize, old_range_end);
|
||||
modified_size = new_range_end - modified_offset;
|
||||
size_t newrangeend = std::max(offset + modifiedsize, oldrangeend);
|
||||
modifiedSize = newrangeend - modifiedOffset;
|
||||
}
|
||||
|
||||
void Buffer::fill(size_t offset, size_t size, const void *data)
|
||||
{
|
||||
memcpy(memory_map + offset, data, size);
|
||||
memcpy(memoryMap + offset, data, size);
|
||||
|
||||
if (is_mapped)
|
||||
if (mapped)
|
||||
setMappedRangeModified(offset, size);
|
||||
else
|
||||
{
|
||||
gl.bindBuffer(type, vbo);
|
||||
gl.bindBuffer(mapType, buffer);
|
||||
glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, data);
|
||||
}
|
||||
}
|
||||
|
||||
ptrdiff_t Buffer::getHandle() const
|
||||
{
|
||||
return vbo;
|
||||
}
|
||||
|
||||
void Buffer::copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset)
|
||||
{
|
||||
other->fill(otheroffset, size, memory_map + offset);
|
||||
}
|
||||
|
||||
bool Buffer::loadVolatile()
|
||||
{
|
||||
return load(true);
|
||||
}
|
||||
|
||||
void Buffer::unloadVolatile()
|
||||
{
|
||||
unload();
|
||||
}
|
||||
|
||||
bool Buffer::load(bool restore)
|
||||
{
|
||||
glGenBuffers(1, &vbo);
|
||||
gl.bindBuffer(type, vbo);
|
||||
|
||||
while (glGetError() != GL_NO_ERROR)
|
||||
/* Clear the error buffer. */;
|
||||
|
||||
// Copy the old buffer only if 'restore' was requested.
|
||||
const GLvoid *src = restore ? memory_map : nullptr;
|
||||
|
||||
// Note that if 'src' is '0', no data will be copied.
|
||||
glBufferData(target, (GLsizeiptr) getSize(), src, OpenGL::getGLBufferUsage(getUsage()));
|
||||
|
||||
return (glGetError() == GL_NO_ERROR);
|
||||
}
|
||||
|
||||
void Buffer::unload()
|
||||
{
|
||||
is_mapped = false;
|
||||
gl.deleteBuffer(vbo);
|
||||
vbo = 0;
|
||||
other->fill(otheroffset, size, memoryMap + offset);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -32,6 +32,9 @@ namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
class Graphics;
|
||||
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
@@ -39,39 +42,45 @@ class Buffer final : public love::graphics::Buffer, public Volatile
|
||||
{
|
||||
public:
|
||||
|
||||
Buffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags);
|
||||
Buffer(love::graphics::Graphics *gfx, const Settings &settings, const std::vector<DataDeclaration> &format, const void *data, size_t size, size_t arraylength);
|
||||
virtual ~Buffer();
|
||||
|
||||
void *map() override;
|
||||
void unmap() override;
|
||||
void setMappedRangeModified(size_t offset, size_t size) override;
|
||||
void fill(size_t offset, size_t size, const void *data) override;
|
||||
ptrdiff_t getHandle() const override;
|
||||
|
||||
void copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset) override;
|
||||
|
||||
// Implements Volatile.
|
||||
bool loadVolatile() override;
|
||||
void unloadVolatile() override;
|
||||
|
||||
void *map() override;
|
||||
void unmap() override;
|
||||
void setMappedRangeModified(size_t offset, size_t size) override;
|
||||
void fill(size_t offset, size_t size, const void *data) override;
|
||||
|
||||
ptrdiff_t getHandle() const override { return buffer; };
|
||||
ptrdiff_t getTexelBufferHandle() const override { return texture; };
|
||||
|
||||
void copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset) override;
|
||||
|
||||
private:
|
||||
|
||||
bool load(bool restore);
|
||||
void unload();
|
||||
|
||||
void unmapStatic(size_t offset, size_t size);
|
||||
void unmapStream();
|
||||
|
||||
GLenum target;
|
||||
BufferType mapType = BUFFERTYPE_VERTEX;
|
||||
GLenum target = 0;
|
||||
|
||||
// The VBO identifier. Assigned by OpenGL.
|
||||
GLuint vbo;
|
||||
// The buffer object identifier. Assigned by OpenGL.
|
||||
GLuint buffer = 0;
|
||||
|
||||
// Used for Texel Buffer types.
|
||||
GLuint texture = 0;
|
||||
|
||||
// A pointer to mapped memory.
|
||||
char *memory_map;
|
||||
char *memoryMap = nullptr;
|
||||
|
||||
size_t modified_offset;
|
||||
size_t modified_size;
|
||||
size_t modifiedOffset = 0;
|
||||
size_t modifiedSize = 0;
|
||||
bool isMappedDataModified = false;
|
||||
|
||||
}; // Buffer
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ love::graphics::Graphics *createInstance()
|
||||
Graphics::Graphics()
|
||||
: windowHasStencil(false)
|
||||
, mainVAO(0)
|
||||
, defaultBuffers()
|
||||
, supportedFormats()
|
||||
{
|
||||
gl = OpenGL();
|
||||
@@ -162,9 +163,9 @@ love::graphics::Shader *Graphics::newShaderInternal(love::graphics::ShaderStage
|
||||
return new Shader(vertex, pixel);
|
||||
}
|
||||
|
||||
love::graphics::Buffer *Graphics::newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags)
|
||||
love::graphics::Buffer *Graphics::newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength)
|
||||
{
|
||||
return new Buffer(size, data, type, usage, mapflags);
|
||||
return new Buffer(this, settings, format, data, size, arraylength);
|
||||
}
|
||||
|
||||
void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight)
|
||||
@@ -255,11 +256,32 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
|
||||
{
|
||||
// Initial sizes that should be good enough for most cases. It will
|
||||
// resize to fit if needed, later.
|
||||
batchedDrawState.vb[0] = CreateStreamBuffer(BUFFER_VERTEX, 1024 * 1024 * 1);
|
||||
batchedDrawState.vb[1] = CreateStreamBuffer(BUFFER_VERTEX, 256 * 1024 * 1);
|
||||
batchedDrawState.indexBuffer = CreateStreamBuffer(BUFFER_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
|
||||
batchedDrawState.vb[0] = CreateStreamBuffer(BUFFERTYPE_VERTEX, 1024 * 1024 * 1);
|
||||
batchedDrawState.vb[1] = CreateStreamBuffer(BUFFERTYPE_VERTEX, 256 * 1024 * 1);
|
||||
batchedDrawState.indexBuffer = CreateStreamBuffer(BUFFERTYPE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
|
||||
}
|
||||
|
||||
if (capabilities.features[FEATURE_TEXEL_BUFFER] && defaultBuffers[BUFFERTYPE_TEXEL].get() == nullptr)
|
||||
{
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_TEXEL, 0, BUFFERUSAGE_STATIC);
|
||||
std::vector<Buffer::DataDeclaration> format = {{"", DATAFORMAT_FLOAT_VEC4, 0}};
|
||||
|
||||
const float texel[] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
|
||||
love::graphics::Buffer *buffer = newBuffer(settings, format, texel, sizeof(texel), 1);
|
||||
defaultBuffers[BUFFERTYPE_TEXEL].set(buffer, Acquire::NORETAIN);
|
||||
}
|
||||
|
||||
// Load default resources before other Volatile.
|
||||
for (int i = 0; i < BUFFERTYPE_MAX_ENUM; i++)
|
||||
{
|
||||
if (defaultBuffers[i].get())
|
||||
((Buffer *) defaultBuffers[i].get())->loadVolatile();
|
||||
}
|
||||
|
||||
if (defaultBuffers[BUFFERTYPE_TEXEL].get())
|
||||
gl.setDefaultTexelBuffer((GLuint) defaultBuffers[BUFFERTYPE_TEXEL]->getTexelBufferHandle());
|
||||
|
||||
// Reload all volatile objects.
|
||||
if (!Volatile::loadAll())
|
||||
::printf("Could not reload all volatile objects.\n");
|
||||
@@ -269,12 +291,11 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
|
||||
// Restore the graphics state.
|
||||
restoreState(states.back());
|
||||
|
||||
int gammacorrect = isGammaCorrect() ? 1 : 0;
|
||||
Shader::Language target = getShaderLanguageTarget();
|
||||
|
||||
// We always need a default shader.
|
||||
for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++)
|
||||
{
|
||||
auto stype = (Shader::StandardShader) i;
|
||||
|
||||
if (i == Shader::STANDARD_ARRAY && !capabilities.textureTypes[TEXTURE_2D_ARRAY])
|
||||
continue;
|
||||
|
||||
@@ -284,8 +305,10 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
|
||||
{
|
||||
if (!Shader::standardShaders[i])
|
||||
{
|
||||
const auto &code = defaultShaderCode[i][target][gammacorrect];
|
||||
Shader::standardShaders[i] = love::graphics::Graphics::newShader(code.source[ShaderStage::STAGE_VERTEX], code.source[ShaderStage::STAGE_PIXEL]);
|
||||
std::vector<std::string> stages;
|
||||
stages.push_back(Shader::getDefaultCode(stype, ShaderStage::STAGE_VERTEX));
|
||||
stages.push_back(Shader::getDefaultCode(stype, ShaderStage::STAGE_PIXEL));
|
||||
Shader::standardShaders[i] = newShader(stages);
|
||||
}
|
||||
}
|
||||
catch (love::Exception &)
|
||||
@@ -376,7 +399,7 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
|
||||
GLenum glprimitivetype = OpenGL::getGLPrimitiveType(cmd.primitiveType);
|
||||
GLenum gldatatype = OpenGL::getGLIndexDataType(cmd.indexType);
|
||||
|
||||
gl.bindBuffer(BUFFER_INDEX, cmd.indexBuffer->getHandle());
|
||||
gl.bindBuffer(BUFFERTYPE_INDEX, cmd.indexBuffer->getHandle());
|
||||
|
||||
if (cmd.instanceCount > 1)
|
||||
glDrawElementsInstanced(glprimitivetype, cmd.indexCount, gldatatype, gloffset, cmd.instanceCount);
|
||||
@@ -386,13 +409,13 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
|
||||
++drawCalls;
|
||||
}
|
||||
|
||||
static inline void advanceVertexOffsets(const vertex::Attributes &attributes, vertex::BufferBindings &buffers, int vertexcount)
|
||||
static inline void advanceVertexOffsets(const VertexAttributes &attributes, BufferBindings &buffers, int vertexcount)
|
||||
{
|
||||
// TODO: Figure out a better way to avoid touching the same buffer multiple
|
||||
// times, if multiple attributes share the buffer.
|
||||
uint32 touchedbuffers = 0;
|
||||
|
||||
for (unsigned int i = 0; i < vertex::Attributes::MAX; i++)
|
||||
for (unsigned int i = 0; i < VertexAttributes::MAX; i++)
|
||||
{
|
||||
if (!attributes.isEnabled(i))
|
||||
continue;
|
||||
@@ -409,7 +432,7 @@ static inline void advanceVertexOffsets(const vertex::Attributes &attributes, ve
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, love::graphics::Texture *texture)
|
||||
void Graphics::drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, love::graphics::Texture *texture)
|
||||
{
|
||||
const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
|
||||
const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
|
||||
@@ -418,7 +441,7 @@ void Graphics::drawQuads(int start, int count, const vertex::Attributes &attribu
|
||||
gl.bindTextureToUnit(texture, 0, false);
|
||||
gl.setCullMode(CULL_NONE);
|
||||
|
||||
gl.bindBuffer(BUFFER_INDEX, quadIndexBuffer->getHandle());
|
||||
gl.bindBuffer(BUFFERTYPE_INDEX, quadIndexBuffer->getHandle());
|
||||
|
||||
if (gl.isBaseVertexSupported())
|
||||
{
|
||||
@@ -438,7 +461,7 @@ void Graphics::drawQuads(int start, int count, const vertex::Attributes &attribu
|
||||
}
|
||||
else
|
||||
{
|
||||
vertex::BufferBindings bufferscopy = buffers;
|
||||
BufferBindings bufferscopy = buffers;
|
||||
if (start > 0)
|
||||
advanceVertexOffsets(attributes, bufferscopy, start * 4);
|
||||
|
||||
@@ -525,7 +548,7 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h,
|
||||
endPass();
|
||||
|
||||
bool iswindow = rts.getFirstTarget().texture == nullptr;
|
||||
vertex::Winding vertexwinding = state.winding;
|
||||
Winding vertexwinding = state.winding;
|
||||
|
||||
if (iswindow)
|
||||
{
|
||||
@@ -543,10 +566,10 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h,
|
||||
|
||||
// Flip front face winding when rendering to a texture, since our
|
||||
// projection matrix is flipped.
|
||||
vertexwinding = vertexwinding == vertex::WINDING_CW ? vertex::WINDING_CCW : vertex::WINDING_CW;
|
||||
vertexwinding = vertexwinding == WINDING_CW ? WINDING_CCW : WINDING_CW;
|
||||
}
|
||||
|
||||
glFrontFace(vertexwinding == vertex::WINDING_CW ? GL_CW : GL_CCW);
|
||||
glFrontFace(vertexwinding == WINDING_CW ? GL_CW : GL_CCW);
|
||||
|
||||
gl.setViewport({0, 0, pixelw, pixelh});
|
||||
|
||||
@@ -1246,7 +1269,7 @@ void Graphics::setDepthMode(CompareMode compare, bool write)
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::setFrontFaceWinding(vertex::Winding winding)
|
||||
void Graphics::setFrontFaceWinding(Winding winding)
|
||||
{
|
||||
DisplayState &state = states.back();
|
||||
|
||||
@@ -1256,9 +1279,9 @@ void Graphics::setFrontFaceWinding(vertex::Winding winding)
|
||||
state.winding = winding;
|
||||
|
||||
if (isRenderTargetActive())
|
||||
winding = winding == vertex::WINDING_CW ? vertex::WINDING_CCW : vertex::WINDING_CW;
|
||||
winding = winding == WINDING_CW ? WINDING_CCW : WINDING_CW;
|
||||
|
||||
glFrontFace(winding == vertex::WINDING_CW ? GL_CW : GL_CCW);
|
||||
glFrontFace(winding == WINDING_CW ? GL_CW : GL_CCW);
|
||||
}
|
||||
|
||||
void Graphics::setColor(Colorf c)
|
||||
@@ -1388,17 +1411,19 @@ void Graphics::initCapabilities()
|
||||
capabilities.features[FEATURE_GLSL3] = GLAD_ES_VERSION_3_0 || gl.isCoreProfile();
|
||||
capabilities.features[FEATURE_GLSL4] = GLAD_ES_VERSION_3_1 || (gl.isCoreProfile() && GLAD_VERSION_4_3);
|
||||
capabilities.features[FEATURE_INSTANCING] = gl.isInstancingSupported();
|
||||
static_assert(FEATURE_MAX_ENUM == 10, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
|
||||
capabilities.features[FEATURE_TEXEL_BUFFER] = gl.areTexelBuffersSupported();
|
||||
static_assert(FEATURE_MAX_ENUM == 11, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
|
||||
|
||||
capabilities.limits[LIMIT_POINT_SIZE] = gl.getMaxPointSize();
|
||||
capabilities.limits[LIMIT_TEXTURE_SIZE] = gl.getMax2DTextureSize();
|
||||
capabilities.limits[LIMIT_TEXTURE_LAYERS] = gl.getMaxTextureLayers();
|
||||
capabilities.limits[LIMIT_VOLUME_TEXTURE_SIZE] = gl.getMax3DTextureSize();
|
||||
capabilities.limits[LIMIT_CUBE_TEXTURE_SIZE] = gl.getMaxCubeTextureSize();
|
||||
capabilities.limits[LIMIT_TEXEL_BUFFER_SIZE] = gl.getMaxTexelBufferSize();
|
||||
capabilities.limits[LIMIT_RENDER_TARGETS] = gl.getMaxRenderTargets();
|
||||
capabilities.limits[LIMIT_TEXTURE_MSAA] = gl.getMaxSamples();
|
||||
capabilities.limits[LIMIT_ANISOTROPY] = gl.getMaxAnisotropy();
|
||||
static_assert(LIMIT_MAX_ENUM == 8, "Graphics::initCapabilities must be updated when adding a new system limit!");
|
||||
static_assert(LIMIT_MAX_ENUM == 9, "Graphics::initCapabilities must be updated when adding a new system limit!");
|
||||
|
||||
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
|
||||
capabilities.textureTypes[i] = gl.isTextureTypeSupported((TextureType) i);
|
||||
@@ -1524,18 +1549,6 @@ bool Graphics::isPixelFormatSupported(PixelFormat format, bool rendertarget, boo
|
||||
return supported.value;
|
||||
}
|
||||
|
||||
Shader::Language Graphics::getShaderLanguageTarget() const
|
||||
{
|
||||
if (gl.isCoreProfile())
|
||||
return Shader::LANGUAGE_GLSL3;
|
||||
else if (GLAD_ES_VERSION_3_0)
|
||||
return Shader::LANGUAGE_ESSL3;
|
||||
else if (GLAD_ES_VERSION_2_0)
|
||||
return Shader::LANGUAGE_ESSL1;
|
||||
else
|
||||
return Shader::LANGUAGE_GLSL1;
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
const char *getName() const override;
|
||||
|
||||
love::graphics::Texture *newTexture(const Texture::Settings &settings, const Texture::Slices *data = nullptr) override;
|
||||
love::graphics::Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) override;
|
||||
love::graphics::Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) override;
|
||||
|
||||
void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
|
||||
bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil) override;
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
|
||||
void draw(const DrawCommand &cmd) override;
|
||||
void draw(const DrawIndexedCommand &cmd) override;
|
||||
void drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, love::graphics::Texture *texture) override;
|
||||
void drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, love::graphics::Texture *texture) override;
|
||||
|
||||
void clear(OptionalColorf color, OptionalInt stencil, OptionalDouble depth) override;
|
||||
void clear(const std::vector<OptionalColorf> &colors, OptionalInt stencil, OptionalDouble depth) override;
|
||||
@@ -91,7 +91,7 @@ public:
|
||||
|
||||
void setDepthMode(CompareMode compare, bool write) override;
|
||||
|
||||
void setFrontFaceWinding(vertex::Winding winding) override;
|
||||
void setFrontFaceWinding(Winding winding) override;
|
||||
|
||||
void setColorMask(ColorChannelMask mask) override;
|
||||
|
||||
@@ -107,8 +107,6 @@ public:
|
||||
bool usesGLSLES() const override;
|
||||
RendererInfo getRendererInfo() const override;
|
||||
|
||||
Shader::Language getShaderLanguageTarget() const override;
|
||||
|
||||
// Internal use.
|
||||
void cleanupRenderTexture(love::graphics::Texture *texture);
|
||||
|
||||
@@ -150,6 +148,9 @@ private:
|
||||
bool windowHasStencil;
|
||||
GLuint mainVAO;
|
||||
|
||||
// Only needed for buffer types that can be bound to shaders.
|
||||
StrongRef<love::graphics::Buffer> defaultBuffers[BUFFERTYPE_MAX_ENUM];
|
||||
|
||||
// [rendertarget][readable][srgb]
|
||||
OptionalBool supportedFormats[PIXELFORMAT_MAX_ENUM][2][2][2];
|
||||
|
||||
|
||||
@@ -155,11 +155,11 @@ bool OpenGL::initContext()
|
||||
#ifdef LOVE_WINDOWS
|
||||
if (getVendor() == VENDOR_AMD)
|
||||
{
|
||||
// Radeon HD drivers switched from "ATI Radeon" to "AMD Radeon" around
|
||||
// Radeon drivers switched from "ATI Radeon" to "AMD Radeon" around
|
||||
// the 7000 series. We'll assume this bug doesn't affect those newer
|
||||
// GPUs / drivers.
|
||||
const char *device = (const char *) glGetString(GL_RENDERER);
|
||||
if (strstr(device, "ATI Radeon HD ") || strstr(device, "ATI Mobility Radeon HD"))
|
||||
if (strstr(device, "ATI Radeon") || strstr(device, "ATI Mobility Radeon"))
|
||||
bugs.texStorageBreaksSubImage = true;
|
||||
}
|
||||
#endif
|
||||
@@ -185,7 +185,7 @@ void OpenGL::setupContext()
|
||||
state.enabledAttribArrays = (uint32) ((1ull << uint32(maxvertexattribs)) - 1);
|
||||
state.instancedAttribArrays = 0;
|
||||
|
||||
setVertexAttributes(vertex::Attributes(), vertex::BufferBindings());
|
||||
setVertexAttributes(VertexAttributes(), BufferBindings());
|
||||
|
||||
// Get the current viewport.
|
||||
glGetIntegerv(GL_VIEWPORT, (GLint *) &state.viewport.x);
|
||||
@@ -222,14 +222,14 @@ void OpenGL::setupContext()
|
||||
glGetIntegerv(GL_CULL_FACE_MODE, &faceCull);
|
||||
state.faceCullMode = faceCull;
|
||||
|
||||
for (int i = 0; i < (int) BUFFER_MAX_ENUM; i++)
|
||||
for (int i = 0; i < (int) BUFFERTYPE_MAX_ENUM; i++)
|
||||
{
|
||||
state.boundBuffers[i] = 0;
|
||||
glBindBuffer(getGLBufferType((BufferType) i), 0);
|
||||
}
|
||||
|
||||
// Initialize multiple texture unit support for shaders.
|
||||
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
|
||||
for (int i = 0; i < TEXTURE_MAX_ENUM + 1; i++)
|
||||
{
|
||||
state.boundTextures[i].clear();
|
||||
state.boundTextures[i].resize(maxTextureUnits, 0);
|
||||
@@ -280,6 +280,10 @@ void OpenGL::deInitContext()
|
||||
}
|
||||
}
|
||||
|
||||
if (state.defaultTexelBuffer != 0)
|
||||
gl.deleteTexture(state.defaultTexelBuffer);
|
||||
state.defaultTexelBuffer = 0;
|
||||
|
||||
contextInitialized = false;
|
||||
}
|
||||
|
||||
@@ -459,6 +463,11 @@ void OpenGL::initMaxValues()
|
||||
else
|
||||
maxTextureArrayLayers = 0;
|
||||
|
||||
if (areTexelBuffersSupported())
|
||||
glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &maxTexelBufferSize);
|
||||
else
|
||||
maxTexelBufferSize = 0;
|
||||
|
||||
int maxattachments = 1;
|
||||
int maxdrawbuffers = 1;
|
||||
|
||||
@@ -562,16 +571,11 @@ GLenum OpenGL::getGLPrimitiveType(PrimitiveType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case PRIMITIVE_TRIANGLES:
|
||||
return GL_TRIANGLES;
|
||||
case PRIMITIVE_TRIANGLE_STRIP:
|
||||
return GL_TRIANGLE_STRIP;
|
||||
case PRIMITIVE_TRIANGLE_FAN:
|
||||
return GL_TRIANGLE_FAN;
|
||||
case PRIMITIVE_POINTS:
|
||||
return GL_POINTS;
|
||||
case PRIMITIVE_MAX_ENUM:
|
||||
return GL_ZERO;
|
||||
case PRIMITIVE_TRIANGLES: return GL_TRIANGLES;
|
||||
case PRIMITIVE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP;
|
||||
case PRIMITIVE_TRIANGLE_FAN: return GL_TRIANGLE_FAN;
|
||||
case PRIMITIVE_POINTS: return GL_POINTS;
|
||||
case PRIMITIVE_MAX_ENUM: return GL_ZERO;
|
||||
}
|
||||
|
||||
return GL_ZERO;
|
||||
@@ -581,12 +585,10 @@ GLenum OpenGL::getGLBufferType(BufferType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case BUFFER_VERTEX:
|
||||
return GL_ARRAY_BUFFER;
|
||||
case BUFFER_INDEX:
|
||||
return GL_ELEMENT_ARRAY_BUFFER;
|
||||
case BUFFER_MAX_ENUM:
|
||||
return GL_ZERO;
|
||||
case BUFFERTYPE_VERTEX: return GL_ARRAY_BUFFER;
|
||||
case BUFFERTYPE_INDEX: return GL_ELEMENT_ARRAY_BUFFER;
|
||||
case BUFFERTYPE_TEXEL: return GL_TEXTURE_BUFFER;
|
||||
case BUFFERTYPE_MAX_ENUM: return GL_ZERO;
|
||||
}
|
||||
|
||||
return GL_ZERO;
|
||||
@@ -596,16 +598,11 @@ 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;
|
||||
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_TEXTURE_BUFFER; // Hack
|
||||
}
|
||||
|
||||
return GL_ZERO;
|
||||
@@ -615,74 +612,159 @@ GLenum OpenGL::getGLIndexDataType(IndexDataType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case INDEX_UINT16:
|
||||
return GL_UNSIGNED_SHORT;
|
||||
case INDEX_UINT32:
|
||||
return GL_UNSIGNED_INT;
|
||||
default:
|
||||
return GL_ZERO;
|
||||
case INDEX_UINT16: return GL_UNSIGNED_SHORT;
|
||||
case INDEX_UINT32: return GL_UNSIGNED_INT;
|
||||
default: return GL_ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum OpenGL::getGLVertexDataType(vertex::DataType type, GLboolean &normalized, bool &intformat)
|
||||
GLenum OpenGL::getGLVertexDataType(DataFormat format, int &components, GLboolean &normalized, bool &intformat)
|
||||
{
|
||||
normalized = GL_FALSE;
|
||||
intformat = false;
|
||||
components = 1;
|
||||
|
||||
switch (type)
|
||||
switch (format)
|
||||
{
|
||||
case vertex::DATA_SNORM8:
|
||||
normalized = GL_TRUE;
|
||||
return GL_BYTE;
|
||||
case vertex::DATA_UNORM8:
|
||||
normalized = GL_TRUE;
|
||||
return GL_UNSIGNED_BYTE;
|
||||
case vertex::DATA_INT8:
|
||||
intformat = true;
|
||||
return GL_BYTE;
|
||||
case vertex::DATA_UINT8:
|
||||
intformat = true;
|
||||
return GL_UNSIGNED_BYTE;
|
||||
case vertex::DATA_SNORM16:
|
||||
normalized = GL_TRUE;
|
||||
return GL_SHORT;
|
||||
case vertex::DATA_UNORM16:
|
||||
normalized = GL_TRUE;
|
||||
return GL_UNSIGNED_SHORT;
|
||||
case vertex::DATA_INT16:
|
||||
intformat = true;
|
||||
return GL_SHORT;
|
||||
case vertex::DATA_UINT16:
|
||||
intformat = true;
|
||||
return GL_UNSIGNED_SHORT;
|
||||
case vertex::DATA_INT32:
|
||||
case DATAFORMAT_FLOAT:
|
||||
components = 1;
|
||||
return GL_FLOAT;
|
||||
case DATAFORMAT_FLOAT_VEC2:
|
||||
components = 2;
|
||||
return GL_FLOAT;
|
||||
case DATAFORMAT_FLOAT_VEC3:
|
||||
components = 3;
|
||||
return GL_FLOAT;
|
||||
case DATAFORMAT_FLOAT_VEC4:
|
||||
components = 4;
|
||||
return GL_FLOAT;
|
||||
|
||||
case DATAFORMAT_FLOAT_MAT2X2:
|
||||
case DATAFORMAT_FLOAT_MAT2X3:
|
||||
case DATAFORMAT_FLOAT_MAT2X4:
|
||||
case DATAFORMAT_FLOAT_MAT3X2:
|
||||
case DATAFORMAT_FLOAT_MAT3X3:
|
||||
case DATAFORMAT_FLOAT_MAT3X4:
|
||||
case DATAFORMAT_FLOAT_MAT4X2:
|
||||
case DATAFORMAT_FLOAT_MAT4X3:
|
||||
case DATAFORMAT_FLOAT_MAT4X4:
|
||||
return GL_ZERO;
|
||||
|
||||
case DATAFORMAT_INT32:
|
||||
components = 1;
|
||||
intformat = true;
|
||||
return GL_INT;
|
||||
case vertex::DATA_UINT32:
|
||||
case DATAFORMAT_INT32_VEC2:
|
||||
components = 2;
|
||||
intformat = true;
|
||||
return GL_INT;
|
||||
case DATAFORMAT_INT32_VEC3:
|
||||
components = 3;
|
||||
intformat = true;
|
||||
return GL_INT;
|
||||
case DATAFORMAT_INT32_VEC4:
|
||||
components = 4;
|
||||
intformat = true;
|
||||
return GL_INT;
|
||||
|
||||
case DATAFORMAT_UINT32:
|
||||
components = 1;
|
||||
intformat = true;
|
||||
return GL_UNSIGNED_INT;
|
||||
case vertex::DATA_FLOAT:
|
||||
normalized = GL_FALSE;
|
||||
return GL_FLOAT;
|
||||
case vertex::DATA_MAX_ENUM:
|
||||
case DATAFORMAT_UINT32_VEC2:
|
||||
components = 2;
|
||||
intformat = true;
|
||||
return GL_UNSIGNED_INT;
|
||||
case DATAFORMAT_UINT32_VEC3:
|
||||
components = 3;
|
||||
intformat = true;
|
||||
return GL_UNSIGNED_INT;
|
||||
case DATAFORMAT_UINT32_VEC4:
|
||||
components = 4;
|
||||
intformat = true;
|
||||
return GL_UNSIGNED_INT;
|
||||
|
||||
case DATAFORMAT_SNORM8_VEC4:
|
||||
components = 4;
|
||||
normalized = GL_TRUE;
|
||||
return GL_BYTE;
|
||||
|
||||
case DATAFORMAT_UNORM8_VEC4:
|
||||
components = 4;
|
||||
normalized = GL_TRUE;
|
||||
return GL_UNSIGNED_BYTE;
|
||||
|
||||
case DATAFORMAT_INT8_VEC4:
|
||||
components = 4;
|
||||
intformat = true;
|
||||
return GL_BYTE;
|
||||
|
||||
case DATAFORMAT_UINT8_VEC4:
|
||||
components = 4;
|
||||
intformat = true;
|
||||
return GL_UNSIGNED_BYTE;
|
||||
|
||||
case DATAFORMAT_SNORM16_VEC2:
|
||||
components = 2;
|
||||
normalized = GL_TRUE;
|
||||
return GL_BYTE;
|
||||
case DATAFORMAT_SNORM16_VEC4:
|
||||
components = 4;
|
||||
normalized = GL_TRUE;
|
||||
return GL_BYTE;
|
||||
|
||||
case DATAFORMAT_UNORM16_VEC2:
|
||||
components = 2;
|
||||
normalized = GL_TRUE;
|
||||
return GL_UNSIGNED_SHORT;
|
||||
case DATAFORMAT_UNORM16_VEC4:
|
||||
components = 4;
|
||||
normalized = GL_TRUE;
|
||||
return GL_UNSIGNED_SHORT;
|
||||
|
||||
case DATAFORMAT_INT16_VEC2:
|
||||
components = 2;
|
||||
intformat = true;
|
||||
return GL_SHORT;
|
||||
case DATAFORMAT_INT16_VEC4:
|
||||
components = 4;
|
||||
intformat = true;
|
||||
return GL_SHORT;
|
||||
|
||||
case DATAFORMAT_UINT16:
|
||||
components = 1;
|
||||
intformat = true;
|
||||
return GL_UNSIGNED_SHORT;
|
||||
case DATAFORMAT_UINT16_VEC2:
|
||||
components = 2;
|
||||
intformat = true;
|
||||
return GL_UNSIGNED_SHORT;
|
||||
case DATAFORMAT_UINT16_VEC4:
|
||||
components = 4;
|
||||
intformat = true;
|
||||
return GL_UNSIGNED_SHORT;
|
||||
|
||||
case DATAFORMAT_BOOL:
|
||||
case DATAFORMAT_BOOL_VEC2:
|
||||
case DATAFORMAT_BOOL_VEC3:
|
||||
case DATAFORMAT_BOOL_VEC4:
|
||||
return GL_ZERO;
|
||||
|
||||
case DATAFORMAT_MAX_ENUM:
|
||||
return GL_ZERO;
|
||||
}
|
||||
|
||||
return GL_ZERO;
|
||||
}
|
||||
|
||||
GLenum OpenGL::getGLBufferUsage(vertex::Usage usage)
|
||||
GLenum OpenGL::getGLBufferUsage(BufferUsage usage)
|
||||
{
|
||||
switch (usage)
|
||||
{
|
||||
case vertex::USAGE_STREAM:
|
||||
return GL_STREAM_DRAW;
|
||||
case vertex::USAGE_DYNAMIC:
|
||||
return GL_DYNAMIC_DRAW;
|
||||
case vertex::USAGE_STATIC:
|
||||
return GL_STATIC_DRAW;
|
||||
default:
|
||||
return 0;
|
||||
case BUFFERUSAGE_STREAM: return GL_STREAM_DRAW;
|
||||
case BUFFERUSAGE_DYNAMIC: return GL_DYNAMIC_DRAW;
|
||||
case BUFFERUSAGE_STATIC: return GL_STATIC_DRAW;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -699,14 +781,14 @@ void OpenGL::deleteBuffer(GLuint buffer)
|
||||
{
|
||||
glDeleteBuffers(1, &buffer);
|
||||
|
||||
for (int i = 0; i < (int) BUFFER_MAX_ENUM; i++)
|
||||
for (int i = 0; i < (int) BUFFERTYPE_MAX_ENUM; i++)
|
||||
{
|
||||
if (state.boundBuffers[i] == buffer)
|
||||
state.boundBuffers[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGL::setVertexAttributes(const vertex::Attributes &attributes, const vertex::BufferBindings &buffers)
|
||||
void OpenGL::setVertexAttributes(const VertexAttributes &attributes, const BufferBindings &buffers)
|
||||
{
|
||||
uint32 enablediff = attributes.enableBits ^ state.enabledAttribArrays;
|
||||
uint32 instanceattribbits = 0;
|
||||
@@ -739,18 +821,19 @@ void OpenGL::setVertexAttributes(const vertex::Attributes &attributes, const ver
|
||||
if ((state.instancedAttribArrays & bit) ^ divisorbit)
|
||||
glVertexAttribDivisor(i, divisor);
|
||||
|
||||
int components = 0;
|
||||
GLboolean normalized = GL_FALSE;
|
||||
bool intformat = false;
|
||||
GLenum gltype = getGLVertexDataType(attrib.type, normalized, intformat);
|
||||
GLenum gltype = getGLVertexDataType(attrib.format, components, normalized, intformat);
|
||||
|
||||
const void *offsetpointer = reinterpret_cast<void*>(bufferinfo.offset + attrib.offsetFromVertex);
|
||||
|
||||
bindBuffer(BUFFER_VERTEX, (GLuint) bufferinfo.buffer->getHandle());
|
||||
bindBuffer(BUFFERTYPE_VERTEX, (GLuint) bufferinfo.buffer->getHandle());
|
||||
|
||||
if (intformat)
|
||||
glVertexAttribIPointer(i, attrib.components, gltype, layout.stride, offsetpointer);
|
||||
glVertexAttribIPointer(i, components, gltype, layout.stride, offsetpointer);
|
||||
else
|
||||
glVertexAttribPointer(i, attrib.components, gltype, normalized, layout.stride, offsetpointer);
|
||||
glVertexAttribPointer(i, components, gltype, normalized, layout.stride, offsetpointer);
|
||||
}
|
||||
|
||||
i++;
|
||||
@@ -988,7 +1071,7 @@ void OpenGL::setTextureUnit(int textureunit)
|
||||
state.curTextureUnit = textureunit;
|
||||
}
|
||||
|
||||
void OpenGL::bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev)
|
||||
void OpenGL::bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev, bool bindforedit)
|
||||
{
|
||||
if (texture != state.boundTextures[target][textureunit])
|
||||
{
|
||||
@@ -1004,9 +1087,19 @@ void OpenGL::bindTextureToUnit(TextureType target, GLuint texture, int textureun
|
||||
else
|
||||
state.curTextureUnit = textureunit;
|
||||
}
|
||||
else if (bindforedit && !restoreprev && textureunit != state.curTextureUnit)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + textureunit);
|
||||
state.curTextureUnit = textureunit;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev)
|
||||
void OpenGL::bindBufferTextureToUnit(GLuint texture, int textureunit, bool restoreprev, bool bindforedit)
|
||||
{
|
||||
bindTextureToUnit(TEXTURE_MAX_ENUM, texture, textureunit, restoreprev, bindforedit);
|
||||
}
|
||||
|
||||
void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev, bool bindforedit)
|
||||
{
|
||||
TextureType textype = TEXTURE_2D;
|
||||
GLuint handle = 0;
|
||||
@@ -1028,14 +1121,14 @@ void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restorepr
|
||||
handle = getDefaultTexture(textype);
|
||||
}
|
||||
|
||||
bindTextureToUnit(textype, handle, textureunit, restoreprev);
|
||||
bindTextureToUnit(textype, handle, textureunit, restoreprev, bindforedit);
|
||||
}
|
||||
|
||||
void OpenGL::deleteTexture(GLuint texture)
|
||||
{
|
||||
// glDeleteTextures binds texture 0 to all texture units the deleted texture
|
||||
// was bound to before deletion.
|
||||
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
|
||||
for (int i = 0; i < TEXTURE_MAX_ENUM + 1; i++)
|
||||
{
|
||||
for (GLuint &texid : state.boundTextures[i])
|
||||
{
|
||||
@@ -1068,24 +1161,15 @@ GLint OpenGL::getGLCompareMode(CompareMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case COMPARE_LESS:
|
||||
return GL_LESS;
|
||||
case COMPARE_LEQUAL:
|
||||
return GL_LEQUAL;
|
||||
case COMPARE_EQUAL:
|
||||
return GL_EQUAL;
|
||||
case COMPARE_GEQUAL:
|
||||
return GL_GEQUAL;
|
||||
case COMPARE_GREATER:
|
||||
return GL_GREATER;
|
||||
case COMPARE_NOTEQUAL:
|
||||
return GL_NOTEQUAL;
|
||||
case COMPARE_ALWAYS:
|
||||
return GL_ALWAYS;
|
||||
case COMPARE_NEVER:
|
||||
return GL_NEVER;
|
||||
default:
|
||||
return GL_NEVER;
|
||||
case COMPARE_LESS: return GL_LESS;
|
||||
case COMPARE_LEQUAL: return GL_LEQUAL;
|
||||
case COMPARE_EQUAL: return GL_EQUAL;
|
||||
case COMPARE_GEQUAL: return GL_GEQUAL;
|
||||
case COMPARE_GREATER: return GL_GREATER;
|
||||
case COMPARE_NOTEQUAL: return GL_NOTEQUAL;
|
||||
case COMPARE_ALWAYS: return GL_ALWAYS;
|
||||
case COMPARE_NEVER: return GL_NEVER;
|
||||
default: return GL_NEVER;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1333,6 +1417,12 @@ bool OpenGL::isMultiFormatMRTSupported() const
|
||||
return getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
|
||||
}
|
||||
|
||||
bool OpenGL::areTexelBuffersSupported() const
|
||||
{
|
||||
// Not supported in ES until 3.2, which we don't support shaders for...
|
||||
return GLAD_VERSION_3_1;
|
||||
}
|
||||
|
||||
int OpenGL::getMax2DTextureSize() const
|
||||
{
|
||||
return std::max(max2DTextureSize, 1);
|
||||
@@ -1353,6 +1443,11 @@ int OpenGL::getMaxTextureLayers() const
|
||||
return std::max(maxTextureArrayLayers, 1);
|
||||
}
|
||||
|
||||
int OpenGL::getMaxTexelBufferSize() const
|
||||
{
|
||||
return maxTexelBufferSize;
|
||||
}
|
||||
|
||||
int OpenGL::getMaxRenderTargets() const
|
||||
{
|
||||
return std::min(maxRenderTargets, MAX_COLOR_RENDER_TARGETS);
|
||||
|
||||
@@ -166,7 +166,7 @@ public:
|
||||
* initial full-size one (determined after some investigation with an
|
||||
* affected user on Discord.)
|
||||
* https://bitbucket.org/rude/love/issues/1436/bug-with-lovegraphicsprint-on-older-ati
|
||||
*
|
||||
* https://github.com/love2d/love/issues/1563
|
||||
**/
|
||||
bool texStorageBreaksSubImage;
|
||||
|
||||
@@ -238,7 +238,7 @@ public:
|
||||
/**
|
||||
* Set all vertex attribute state.
|
||||
**/
|
||||
void setVertexAttributes(const vertex::Attributes &attributes, const vertex::BufferBindings &buffers);
|
||||
void setVertexAttributes(const VertexAttributes &attributes, const BufferBindings &buffers);
|
||||
|
||||
/**
|
||||
* Wrapper for glCullFace which eliminates redundant state setting.
|
||||
@@ -306,6 +306,12 @@ public:
|
||||
**/
|
||||
GLuint getDefaultTexture(TextureType type) const;
|
||||
|
||||
/**
|
||||
* Gets the texture ID for love's default texel buffer.
|
||||
**/
|
||||
GLuint getDefaultTexelBuffer() const { return state.defaultTexelBuffer; }
|
||||
void setDefaultTexelBuffer(GLuint tex) { state.defaultTexelBuffer = tex; }
|
||||
|
||||
/**
|
||||
* Helper for setting the active texture unit.
|
||||
*
|
||||
@@ -318,9 +324,12 @@ public:
|
||||
*
|
||||
* @param textureunit Index in the range of [0, maxtextureunits-1]
|
||||
* @param restoreprev Restore previously bound texture unit when done.
|
||||
* @param bindforedit If false, the active texture unit may be left alone.
|
||||
**/
|
||||
void bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev);
|
||||
void bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev);
|
||||
void bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev, bool bindforedit = true);
|
||||
void bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev, bool bindforedit = true);
|
||||
|
||||
void bindBufferTextureToUnit(GLuint texture, int textureunit, bool restoreprev, bool bindforedit);
|
||||
|
||||
/**
|
||||
* Helper for deleting an OpenGL texture.
|
||||
@@ -348,6 +357,7 @@ public:
|
||||
bool isSamplerLODBiasSupported() const;
|
||||
bool isBaseVertexSupported() const;
|
||||
bool isMultiFormatMRTSupported() const;
|
||||
bool areTexelBuffersSupported() const;
|
||||
|
||||
/**
|
||||
* Returns the maximum supported width or height of a texture.
|
||||
@@ -357,6 +367,11 @@ public:
|
||||
int getMaxCubeTextureSize() const;
|
||||
int getMaxTextureLayers() const;
|
||||
|
||||
/**
|
||||
* Returns the maximum number of values in a texel buffer.
|
||||
**/
|
||||
int getMaxTexelBufferSize() const;
|
||||
|
||||
/**
|
||||
* Returns the maximum supported number of simultaneous render targets.
|
||||
**/
|
||||
@@ -398,8 +413,8 @@ public:
|
||||
static GLenum getGLPrimitiveType(PrimitiveType type);
|
||||
static GLenum getGLBufferType(BufferType type);
|
||||
static GLenum getGLIndexDataType(IndexDataType type);
|
||||
static GLenum getGLVertexDataType(vertex::DataType type, GLboolean &normalized, bool &intformat);
|
||||
static GLenum getGLBufferUsage(vertex::Usage usage);
|
||||
static GLenum getGLVertexDataType(DataFormat format, int &components, GLboolean &normalized, bool &intformat);
|
||||
static GLenum getGLBufferUsage(BufferUsage usage);
|
||||
static GLenum getGLTextureType(TextureType type);
|
||||
static GLint getGLWrapMode(SamplerState::WrapMode wmode);
|
||||
static GLint getGLCompareMode(CompareMode mode);
|
||||
@@ -435,6 +450,7 @@ private:
|
||||
int max3DTextureSize;
|
||||
int maxCubeTextureSize;
|
||||
int maxTextureArrayLayers;
|
||||
int maxTexelBufferSize;
|
||||
int maxRenderTargets;
|
||||
int maxSamples;
|
||||
int maxTextureUnits;
|
||||
@@ -447,10 +463,10 @@ private:
|
||||
// Tracked OpenGL state.
|
||||
struct
|
||||
{
|
||||
GLuint boundBuffers[BUFFER_MAX_ENUM];
|
||||
GLuint boundBuffers[BUFFERTYPE_MAX_ENUM];
|
||||
|
||||
// Texture unit state (currently bound texture for each texture unit.)
|
||||
std::vector<GLuint> boundTextures[TEXTURE_MAX_ENUM];
|
||||
std::vector<GLuint> boundTextures[TEXTURE_MAX_ENUM + 1];
|
||||
|
||||
bool enableState[ENABLE_MAX_ENUM];
|
||||
|
||||
@@ -471,6 +487,7 @@ private:
|
||||
GLuint boundFramebuffers[2];
|
||||
|
||||
GLuint defaultTexture[TEXTURE_MAX_ENUM];
|
||||
GLuint defaultTexelBuffer;
|
||||
|
||||
} state;
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "Shader.h"
|
||||
#include "ShaderStage.h"
|
||||
#include "Graphics.h"
|
||||
#include "graphics/vertex.h"
|
||||
|
||||
// C++
|
||||
#include <algorithm>
|
||||
@@ -68,6 +69,16 @@ Shader::~Shader()
|
||||
|
||||
delete[] p.second.textures;
|
||||
}
|
||||
else if (p.second.baseType == UNIFORM_TEXELBUFFER)
|
||||
{
|
||||
for (int i = 0; i < p.second.count; i++)
|
||||
{
|
||||
if (p.second.buffers[i] != nullptr)
|
||||
p.second.buffers[i]->release();
|
||||
}
|
||||
|
||||
delete[] p.second.buffers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +117,7 @@ void Shader::mapActiveUniforms()
|
||||
u.location = glGetUniformLocation(program, u.name.c_str());
|
||||
u.baseType = getUniformBaseType(gltype);
|
||||
u.textureType = getUniformTextureType(gltype);
|
||||
u.texelBufferType = getUniformTexelBufferType(gltype);
|
||||
u.isDepthSampler = isDepthTextureType(gltype);
|
||||
|
||||
if (u.baseType == UNIFORM_MATRIX)
|
||||
@@ -129,12 +141,22 @@ void Shader::mapActiveUniforms()
|
||||
if (u.location == -1)
|
||||
continue;
|
||||
|
||||
if (u.baseType == UNIFORM_SAMPLER && builtin != BUILTIN_TEXTURE_MAIN)
|
||||
if ((u.baseType == UNIFORM_SAMPLER && builtin != BUILTIN_TEXTURE_MAIN) || u.baseType == UNIFORM_TEXELBUFFER)
|
||||
{
|
||||
TextureUnit unit;
|
||||
unit.type = u.textureType;
|
||||
unit.active = true;
|
||||
unit.texture = gl.getDefaultTexture(u.textureType);
|
||||
|
||||
if (u.baseType == UNIFORM_TEXELBUFFER)
|
||||
{
|
||||
unit.isTexelBuffer = true;
|
||||
unit.texture = gl.getDefaultTexelBuffer();
|
||||
}
|
||||
else
|
||||
{
|
||||
unit.isTexelBuffer = false;
|
||||
unit.texture = gl.getDefaultTexture(u.textureType);
|
||||
}
|
||||
|
||||
for (int i = 0; i < u.count; i++)
|
||||
textureUnits.push_back(unit);
|
||||
@@ -164,6 +186,7 @@ void Shader::mapActiveUniforms()
|
||||
case UNIFORM_INT:
|
||||
case UNIFORM_BOOL:
|
||||
case UNIFORM_SAMPLER:
|
||||
case UNIFORM_TEXELBUFFER:
|
||||
u.dataSize = sizeof(int) * u.components * u.count;
|
||||
u.data = malloc(u.dataSize);
|
||||
break;
|
||||
@@ -183,7 +206,7 @@ void Shader::mapActiveUniforms()
|
||||
{
|
||||
memset(u.data, 0, u.dataSize);
|
||||
|
||||
if (u.baseType == UNIFORM_SAMPLER)
|
||||
if (u.baseType == UNIFORM_SAMPLER || u.baseType == UNIFORM_TEXELBUFFER)
|
||||
{
|
||||
int startunit = (int) textureUnits.size() - u.count;
|
||||
|
||||
@@ -195,8 +218,16 @@ void Shader::mapActiveUniforms()
|
||||
|
||||
glUniform1iv(u.location, u.count, u.ints);
|
||||
|
||||
u.textures = new love::graphics::Texture*[u.count];
|
||||
memset(u.textures, 0, sizeof(Texture *) * u.count);
|
||||
if (u.baseType == UNIFORM_TEXELBUFFER)
|
||||
{
|
||||
u.buffers = new love::graphics::Buffer*[u.count];
|
||||
memset(u.buffers, 0, sizeof(Buffer *) * u.count);
|
||||
}
|
||||
else
|
||||
{
|
||||
u.textures = new love::graphics::Texture*[u.count];
|
||||
memset(u.textures, 0, sizeof(Texture *) * u.count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +288,6 @@ void Shader::mapActiveUniforms()
|
||||
{
|
||||
if (u.textures[i] == nullptr)
|
||||
continue;
|
||||
|
||||
Volatile *v = dynamic_cast<Volatile *>(u.textures[i]);
|
||||
if (v != nullptr)
|
||||
v->loadVolatile();
|
||||
@@ -265,6 +295,19 @@ void Shader::mapActiveUniforms()
|
||||
|
||||
sendTextures(&u, u.textures, u.count, true);
|
||||
}
|
||||
else if (u.baseType == UNIFORM_TEXELBUFFER)
|
||||
{
|
||||
for (int i = 0; i < u.count; i++)
|
||||
{
|
||||
if (u.buffers[i] == nullptr)
|
||||
continue;
|
||||
Volatile *v = dynamic_cast<Volatile *>(u.buffers[i]);
|
||||
if (v != nullptr)
|
||||
v->loadVolatile();
|
||||
}
|
||||
|
||||
sendBuffers(&u, u.buffers, u.count, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure uniforms that existed before but don't exist anymore are
|
||||
@@ -275,16 +318,26 @@ void Shader::mapActiveUniforms()
|
||||
{
|
||||
free(p.second.data);
|
||||
|
||||
if (p.second.baseType != UNIFORM_SAMPLER)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < p.second.count; i++)
|
||||
if (p.second.baseType == UNIFORM_SAMPLER)
|
||||
{
|
||||
if (p.second.textures[i] != nullptr)
|
||||
p.second.textures[i]->release();
|
||||
}
|
||||
for (int i = 0; i < p.second.count; i++)
|
||||
{
|
||||
if (p.second.textures[i] != nullptr)
|
||||
p.second.textures[i]->release();
|
||||
}
|
||||
|
||||
delete[] p.second.textures;
|
||||
delete[] p.second.textures;
|
||||
}
|
||||
else if (p.second.baseType == UNIFORM_TEXELBUFFER)
|
||||
{
|
||||
for (int i = 0; i < p.second.count; i++)
|
||||
{
|
||||
if (p.second.buffers[i] != nullptr)
|
||||
p.second.buffers[i]->release();
|
||||
}
|
||||
|
||||
delete[] p.second.buffers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +375,7 @@ bool Shader::loadVolatile()
|
||||
for (int i = 0; i < int(ATTRIB_MAX_ENUM); i++)
|
||||
{
|
||||
const char *name = nullptr;
|
||||
if (vertex::getConstant((BuiltinVertexAttribute) i, name))
|
||||
if (graphics::getConstant((BuiltinVertexAttribute) i, name))
|
||||
glBindAttribLocation(program, i, (const GLchar *) name);
|
||||
}
|
||||
|
||||
@@ -430,7 +483,12 @@ void Shader::attach()
|
||||
{
|
||||
const TextureUnit &unit = textureUnits[i];
|
||||
if (unit.active)
|
||||
gl.bindTextureToUnit(unit.type, unit.texture, i, false);
|
||||
{
|
||||
if (unit.isTexelBuffer)
|
||||
gl.bindBufferTextureToUnit(unit.texture, i, false, false);
|
||||
else
|
||||
gl.bindTextureToUnit(unit.type, unit.texture, i, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
// send any pending uniforms to the shader program.
|
||||
@@ -493,7 +551,7 @@ void Shader::updateUniform(const UniformInfo *info, int count, bool internalupda
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER)
|
||||
else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER || type == UNIFORM_TEXELBUFFER)
|
||||
{
|
||||
switch (info->components)
|
||||
{
|
||||
@@ -627,7 +685,110 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
|
||||
int texunit = info->ints[i];
|
||||
|
||||
if (shaderactive)
|
||||
gl.bindTextureToUnit(info->textureType, gltex, texunit, false);
|
||||
gl.bindTextureToUnit(info->textureType, gltex, texunit, false, false);
|
||||
|
||||
// Store texture id so it can be re-bound to the texture unit later.
|
||||
textureUnits[texunit].texture = gltex;
|
||||
}
|
||||
}
|
||||
|
||||
void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count)
|
||||
{
|
||||
Shader::sendBuffers(info, buffers, count, false);
|
||||
}
|
||||
|
||||
static bool isTexelBufferTypeCompatible(DataBaseType a, DataBaseType b)
|
||||
{
|
||||
if (a == DATA_BASETYPE_FLOAT || a == DATA_BASETYPE_UNORM || a == DATA_BASETYPE_SNORM)
|
||||
return b == DATA_BASETYPE_FLOAT || b == DATA_BASETYPE_UNORM || b == DATA_BASETYPE_SNORM;
|
||||
|
||||
if (a == DATA_BASETYPE_INT && b == DATA_BASETYPE_INT)
|
||||
return true;
|
||||
|
||||
if (a == DATA_BASETYPE_UINT && b == DATA_BASETYPE_UINT)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count, bool internalUpdate)
|
||||
{
|
||||
if (info->baseType != UNIFORM_TEXELBUFFER)
|
||||
return;
|
||||
|
||||
uint32 requiredtypeflags = Buffer::TYPEFLAG_TEXEL;
|
||||
|
||||
bool shaderactive = current == this;
|
||||
|
||||
if (!internalUpdate && shaderactive)
|
||||
flushBatchedDraws();
|
||||
|
||||
count = std::min(count, info->count);
|
||||
|
||||
// Bind the textures to the texture units.
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
love::graphics::Buffer *buffer = buffers[i];
|
||||
|
||||
if (buffer != nullptr)
|
||||
{
|
||||
if ((buffer->getTypeFlags() & requiredtypeflags) == 0)
|
||||
{
|
||||
if (internalUpdate)
|
||||
continue;
|
||||
else
|
||||
throw love::Exception("Shader uniform '%s' is a texel buffer, but the given Buffer was not created with texel buffer capabilities.", info->name.c_str());
|
||||
}
|
||||
|
||||
DataBaseType basetype = buffer->getDataMember(0).info.baseType;
|
||||
if (!isTexelBufferTypeCompatible(basetype, info->texelBufferType))
|
||||
{
|
||||
if (internalUpdate)
|
||||
continue;
|
||||
else
|
||||
throw love::Exception("Texel buffer's data format base type must match the variable declared in the shader.");
|
||||
}
|
||||
|
||||
buffer->retain();
|
||||
}
|
||||
|
||||
bool addbuffertoarray = true;
|
||||
|
||||
if (info->buffers[i] != nullptr)
|
||||
{
|
||||
Buffer *oldbuffer = info->buffers[i];
|
||||
auto it = std::find(buffersToUnmap.begin(), buffersToUnmap.end(), oldbuffer);
|
||||
if (it != buffersToUnmap.end())
|
||||
{
|
||||
addbuffertoarray = false;
|
||||
if (buffer != nullptr)
|
||||
*it = buffer;
|
||||
else
|
||||
{
|
||||
auto last = buffersToUnmap.end() - 1;
|
||||
*it = *last;
|
||||
buffersToUnmap.erase(last);
|
||||
}
|
||||
}
|
||||
|
||||
oldbuffer->release();
|
||||
}
|
||||
|
||||
if (addbuffertoarray && buffer != nullptr)
|
||||
buffersToUnmap.push_back(buffer);
|
||||
|
||||
info->buffers[i] = buffer;
|
||||
|
||||
GLuint gltex = 0;
|
||||
if (buffers[i] != nullptr)
|
||||
gltex = (GLuint) buffer->getTexelBufferHandle();
|
||||
else
|
||||
gltex = gl.getDefaultTexelBuffer();
|
||||
|
||||
int texunit = info->ints[i];
|
||||
|
||||
if (shaderactive)
|
||||
gl.bindBufferTextureToUnit(gltex, texunit, false, false);
|
||||
|
||||
// Store texture id so it can be re-bound to the texture unit later.
|
||||
textureUnits[texunit].texture = gltex;
|
||||
@@ -746,11 +907,20 @@ void Shader::updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW,
|
||||
GLint location = builtinUniforms[BUILTIN_UNIFORMS_PER_DRAW];
|
||||
if (location >= 0)
|
||||
glUniform4fv(location, 13, (const GLfloat *) &data);
|
||||
|
||||
// TODO: Find a better place to put this.
|
||||
// Buffers used in this shader can be mapped by external code without
|
||||
// unmapping. We need to make sure the data on the GPU is up to date,
|
||||
// otherwise the shader can read from old data.
|
||||
for (Buffer *buffer : buffersToUnmap)
|
||||
buffer->unmap();
|
||||
}
|
||||
|
||||
int Shader::getUniformTypeComponents(GLenum type) const
|
||||
{
|
||||
if (getUniformBaseType(type) == UNIFORM_SAMPLER)
|
||||
UniformType basetype = getUniformBaseType(type);
|
||||
|
||||
if (basetype == UNIFORM_SAMPLER || basetype == UNIFORM_TEXELBUFFER)
|
||||
return 1;
|
||||
|
||||
switch (type)
|
||||
@@ -879,6 +1049,10 @@ Shader::UniformType Shader::getUniformBaseType(GLenum type) const
|
||||
case GL_SAMPLER_CUBE_MAP_ARRAY:
|
||||
case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
|
||||
return UNIFORM_SAMPLER;
|
||||
case GL_SAMPLER_BUFFER:
|
||||
case GL_INT_SAMPLER_BUFFER:
|
||||
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
|
||||
return UNIFORM_TEXELBUFFER;
|
||||
default:
|
||||
return UNIFORM_UNKNOWN;
|
||||
}
|
||||
@@ -922,6 +1096,21 @@ TextureType Shader::getUniformTextureType(GLenum type) const
|
||||
}
|
||||
}
|
||||
|
||||
DataBaseType Shader::getUniformTexelBufferType(GLenum type) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GL_SAMPLER_BUFFER:
|
||||
return DATA_BASETYPE_FLOAT;
|
||||
case GL_INT_SAMPLER_BUFFER:
|
||||
return DATA_BASETYPE_INT;
|
||||
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
|
||||
return DATA_BASETYPE_UINT;
|
||||
default:
|
||||
return DATA_BASETYPE_MAX_ENUM;
|
||||
}
|
||||
}
|
||||
|
||||
bool Shader::isDepthTextureType(GLenum type) const
|
||||
{
|
||||
switch (type)
|
||||
|
||||
@@ -62,6 +62,7 @@ public:
|
||||
const UniformInfo *getUniformInfo(BuiltinUniform builtin) const override;
|
||||
void updateUniform(const UniformInfo *info, int count) override;
|
||||
void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count) override;
|
||||
void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override;
|
||||
bool hasUniform(const std::string &name) const override;
|
||||
ptrdiff_t getHandle() const override;
|
||||
void setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture) override;
|
||||
@@ -75,6 +76,7 @@ private:
|
||||
{
|
||||
GLuint texture = 0;
|
||||
TextureType type = TEXTURE_2D;
|
||||
bool isTexelBuffer = false;
|
||||
bool active = false;
|
||||
};
|
||||
|
||||
@@ -83,11 +85,13 @@ private:
|
||||
|
||||
void updateUniform(const UniformInfo *info, int count, bool internalupdate);
|
||||
void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count, bool internalupdate);
|
||||
void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count, bool internalupdate);
|
||||
|
||||
int getUniformTypeComponents(GLenum type) const;
|
||||
MatrixSize getMatrixSize(GLenum type) const;
|
||||
UniformType getUniformBaseType(GLenum type) const;
|
||||
TextureType getUniformTextureType(GLenum type) const;
|
||||
DataBaseType getUniformTexelBufferType(GLenum type) const;
|
||||
bool isDepthTextureType(GLenum type) const;
|
||||
|
||||
void flushBatchedDraws() const;
|
||||
@@ -112,6 +116,8 @@ private:
|
||||
|
||||
std::vector<std::pair<const UniformInfo *, int>> pendingUniformUpdates;
|
||||
|
||||
std::vector<Buffer *> buffersToUnmap;
|
||||
|
||||
float lastPointSize;
|
||||
|
||||
}; // Shader
|
||||
|
||||
@@ -42,6 +42,11 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo
|
||||
glGenFramebuffers(1, &framebuffer);
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, framebuffer);
|
||||
|
||||
// Intel driver bug: https://github.com/love2d/love/issues/1592
|
||||
bool current_srgb = gl.isStateEnabled(OpenGL::ENABLE_FRAMEBUFFER_SRGB);
|
||||
if (current_srgb && isPixelFormatDepthStencil(format))
|
||||
gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, false);
|
||||
|
||||
if (texture != 0)
|
||||
{
|
||||
if (isPixelFormatDepthStencil(format) && (GLAD_ES_VERSION_3_0 || !GLAD_ES_VERSION_2_0))
|
||||
@@ -103,6 +108,11 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
|
||||
|
||||
// Restore sRGB state if we turned it off above.
|
||||
if (current_srgb && isPixelFormatDepthStencil(format))
|
||||
gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, current_srgb);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
+206
-233
@@ -25,8 +25,6 @@ namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace vertex
|
||||
{
|
||||
|
||||
static_assert(sizeof(Color32) == 4, "sizeof(Color32) incorrect!");
|
||||
static_assert(sizeof(STf_RGBAub) == sizeof(float)*2 + sizeof(Color32), "sizeof(STf_RGBAub) incorrect!");
|
||||
@@ -103,6 +101,68 @@ int getFormatPositionComponents(CommonFormat format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Order here relies on order of DataFormat enum.
|
||||
static const DataFormatInfo dataFormatInfo[]
|
||||
{
|
||||
// baseType, isMatrix, components, rows, columns, componentSize, size
|
||||
{ DATA_BASETYPE_FLOAT, false, 1, 0, 0, 4, 4 }, // DATAFORMAT_FLOAT
|
||||
{ DATA_BASETYPE_FLOAT, false, 2, 0, 0, 4, 8 }, // DATAFORMAT_FLOAT_VEC2
|
||||
{ DATA_BASETYPE_FLOAT, false, 3, 0, 0, 4, 12 }, // DATAFORMAT_FLOAT_VEC3
|
||||
{ DATA_BASETYPE_FLOAT, false, 4, 0, 0, 4, 16 }, // DATAFORMAT_FLOAT_VEC4
|
||||
|
||||
{ DATA_BASETYPE_FLOAT, true, 0, 2, 2, 4, 16 }, // DATAFORMAT_FLOAT_MAT2X2
|
||||
{ DATA_BASETYPE_FLOAT, true, 0, 2, 3, 4, 24 }, // DATAFORMAT_FLOAT_MAT2X3
|
||||
{ DATA_BASETYPE_FLOAT, true, 0, 2, 4, 4, 32 }, // DATAFORMAT_FLOAT_MAT2X4
|
||||
|
||||
{ DATA_BASETYPE_FLOAT, true, 0, 3, 2, 4, 24 }, // DATAFORMAT_FLOAT_MAT3X2
|
||||
{ DATA_BASETYPE_FLOAT, true, 0, 3, 3, 4, 36 }, // DATAFORMAT_FLOAT_MAT3X3
|
||||
{ DATA_BASETYPE_FLOAT, true, 0, 3, 4, 4, 48 }, // DATAFORMAT_FLOAT_MAT3X4
|
||||
|
||||
{ DATA_BASETYPE_FLOAT, true, 0, 4, 2, 4, 32 }, // DATAFORMAT_FLOAT_MAT4X2
|
||||
{ DATA_BASETYPE_FLOAT, true, 0, 4, 3, 4, 48 }, // DATAFORMAT_FLOAT_MAT4X3
|
||||
{ DATA_BASETYPE_FLOAT, true, 0, 4, 4, 4, 64 }, // DATAFORMAT_FLOAT_MAT4X4
|
||||
|
||||
{ DATA_BASETYPE_INT, false, 1, 0, 0, 4, 4 }, // DATAFORMAT_INT32
|
||||
{ DATA_BASETYPE_INT, false, 2, 0, 0, 4, 8 }, // DATAFORMAT_INT32_VEC2
|
||||
{ DATA_BASETYPE_INT, false, 3, 0, 0, 4, 12 }, // DATAFORMAT_INT32_VEC3
|
||||
{ DATA_BASETYPE_INT, false, 4, 0, 0, 4, 16 }, // DATAFORMAT_INT32_VEC4
|
||||
|
||||
{ DATA_BASETYPE_UINT, false, 1, 0, 0, 4, 4 }, // DATAFORMAT_UINT32
|
||||
{ DATA_BASETYPE_UINT, false, 2, 0, 0, 4, 8 }, // DATAFORMAT_UINT32_VEC2
|
||||
{ DATA_BASETYPE_UINT, false, 3, 0, 0, 4, 12 }, // DATAFORMAT_UINT32_VEC3
|
||||
{ DATA_BASETYPE_UINT, false, 4, 0, 0, 4, 16 }, // DATAFORMAT_UINT32_VEC4
|
||||
|
||||
{ DATA_BASETYPE_SNORM, false, 4, 0, 0, 1, 4 }, // DATAFORMAT_SNORM8_VEC4
|
||||
{ DATA_BASETYPE_UNORM, false, 4, 0, 0, 1, 4 }, // DATAFORMAT_UNORM8_VEC4
|
||||
{ DATA_BASETYPE_INT, false, 4, 0, 0, 1, 4 }, // DATAFORMAT_INT8_VEC4
|
||||
{ DATA_BASETYPE_UINT, false, 4, 0, 0, 1, 4 }, // DATAFORMAT_UINT8_VEC4
|
||||
|
||||
{ DATA_BASETYPE_SNORM, false, 2, 0, 0, 2, 4 }, // DATAFORMAT_SNORM16_VEC2
|
||||
{ DATA_BASETYPE_SNORM, false, 4, 0, 0, 2, 8 }, // DATAFORMAT_SNORM16_VEC4
|
||||
|
||||
{ DATA_BASETYPE_UNORM, false, 2, 0, 0, 2, 4 }, // DATAFORMAT_UNORM16_VEC2
|
||||
{ DATA_BASETYPE_UNORM, false, 4, 0, 0, 2, 8 }, // DATAFORMAT_UNORM16_VEC4
|
||||
|
||||
{ DATA_BASETYPE_INT, false, 2, 0, 0, 2, 4 }, // DATAFORMAT_INT16_VEC2
|
||||
{ DATA_BASETYPE_INT, false, 4, 0, 0, 2, 8 }, // DATAFORMAT_INT16_VEC4
|
||||
|
||||
{ DATA_BASETYPE_UINT, false, 1, 0, 0, 2, 2 }, // DATAFORMAT_UINT16
|
||||
{ DATA_BASETYPE_UINT, false, 2, 0, 0, 2, 4 }, // DATAFORMAT_UINT16_VEC2
|
||||
{ DATA_BASETYPE_UINT, false, 4, 0, 0, 2, 8 }, // DATAFORMAT_UINT16_VEC4
|
||||
|
||||
{ DATA_BASETYPE_BOOL, false, 1, 0, 0, 4, 4 }, // DATAFORMAT_BOOL
|
||||
{ DATA_BASETYPE_BOOL, false, 2, 0, 0, 4, 8 }, // DATAFORMAT_BOOL_VEC2
|
||||
{ DATA_BASETYPE_BOOL, false, 3, 0, 0, 4, 12 }, // DATAFORMAT_BOOL_VEC3
|
||||
{ DATA_BASETYPE_BOOL, false, 4, 0, 0, 4, 16 }, // DATAFORMAT_BOOL_VEC4
|
||||
};
|
||||
|
||||
static_assert((sizeof(dataFormatInfo) / sizeof(DataFormatInfo)) == DATAFORMAT_MAX_ENUM, "dataFormatInfo array size must match number of DataFormat enum values.");
|
||||
|
||||
const DataFormatInfo &getDataFormatInfo(DataFormat format)
|
||||
{
|
||||
return dataFormatInfo[format];
|
||||
}
|
||||
|
||||
size_t getIndexDataSize(IndexDataType type)
|
||||
{
|
||||
switch (type)
|
||||
@@ -113,63 +173,36 @@ size_t getIndexDataSize(IndexDataType type)
|
||||
}
|
||||
}
|
||||
|
||||
size_t getDataTypeSize(DataType datatype)
|
||||
{
|
||||
switch (datatype)
|
||||
{
|
||||
case DATA_SNORM8:
|
||||
case DATA_UNORM8:
|
||||
case DATA_INT8:
|
||||
case DATA_UINT8:
|
||||
return sizeof(uint8);
|
||||
case DATA_SNORM16:
|
||||
case DATA_UNORM16:
|
||||
case DATA_INT16:
|
||||
case DATA_UINT16:
|
||||
return sizeof(uint16);
|
||||
case DATA_INT32:
|
||||
case DATA_UINT32:
|
||||
return sizeof(uint32);
|
||||
case DATA_FLOAT:
|
||||
return sizeof(float);
|
||||
case DATA_MAX_ENUM:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isDataTypeInteger(DataType datatype)
|
||||
{
|
||||
switch (datatype)
|
||||
{
|
||||
case DATA_INT8:
|
||||
case DATA_UINT8:
|
||||
case DATA_INT16:
|
||||
case DATA_UINT16:
|
||||
case DATA_INT32:
|
||||
case DATA_UINT32:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
IndexDataType getIndexDataTypeFromMax(size_t maxvalue)
|
||||
{
|
||||
IndexDataType types[] = {INDEX_UINT16, INDEX_UINT32};
|
||||
return types[maxvalue > LOVE_UINT16_MAX ? 1 : 0];
|
||||
return maxvalue > LOVE_UINT16_MAX ? INDEX_UINT32 : INDEX_UINT16;
|
||||
}
|
||||
|
||||
DataFormat getIndexDataFormat(IndexDataType type)
|
||||
{
|
||||
return type == INDEX_UINT32 ? DATAFORMAT_UINT32 : DATAFORMAT_UINT16;
|
||||
}
|
||||
|
||||
IndexDataType getIndexDataType(DataFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case DATAFORMAT_UINT16: return INDEX_UINT16;
|
||||
case DATAFORMAT_UINT32: return INDEX_UINT32;
|
||||
default: return INDEX_MAX_ENUM;
|
||||
}
|
||||
}
|
||||
|
||||
int getIndexCount(TriangleIndexMode mode, int vertexCount)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case TriangleIndexMode::NONE:
|
||||
case TRIANGLEINDEX_NONE:
|
||||
return 0;
|
||||
case TriangleIndexMode::STRIP:
|
||||
case TriangleIndexMode::FAN:
|
||||
case TRIANGLEINDEX_STRIP:
|
||||
case TRIANGLEINDEX_FAN:
|
||||
return 3 * (vertexCount - 2);
|
||||
case TriangleIndexMode::QUADS:
|
||||
case TRIANGLEINDEX_QUADS:
|
||||
return vertexCount * 6 / 4;
|
||||
}
|
||||
return 0;
|
||||
@@ -180,9 +213,9 @@ static void fillIndicesT(TriangleIndexMode mode, T vertexStart, T vertexCount, T
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case TriangleIndexMode::NONE:
|
||||
case TRIANGLEINDEX_NONE:
|
||||
break;
|
||||
case TriangleIndexMode::STRIP:
|
||||
case TRIANGLEINDEX_STRIP:
|
||||
{
|
||||
int i = 0;
|
||||
for (T index = 0; index < vertexCount - 2; index++)
|
||||
@@ -193,7 +226,7 @@ static void fillIndicesT(TriangleIndexMode mode, T vertexStart, T vertexCount, T
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TriangleIndexMode::FAN:
|
||||
case TRIANGLEINDEX_FAN:
|
||||
{
|
||||
int i = 0;
|
||||
for (T index = 2; index < vertexCount; index++)
|
||||
@@ -204,7 +237,7 @@ static void fillIndicesT(TriangleIndexMode mode, T vertexStart, T vertexCount, T
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TriangleIndexMode::QUADS:
|
||||
case TRIANGLEINDEX_QUADS:
|
||||
{
|
||||
// 0---2
|
||||
// | / |
|
||||
@@ -238,7 +271,7 @@ void fillIndices(TriangleIndexMode mode, uint32 vertexStart, uint32 vertexCount,
|
||||
fillIndicesT(mode, vertexStart, vertexCount, indices);
|
||||
}
|
||||
|
||||
void Attributes::setCommonFormat(CommonFormat format, uint8 bufferindex)
|
||||
void VertexAttributes::setCommonFormat(CommonFormat format, uint8 bufferindex)
|
||||
{
|
||||
setBufferLayout(bufferindex, (uint16) getFormatStride(format));
|
||||
|
||||
@@ -247,241 +280,181 @@ void Attributes::setCommonFormat(CommonFormat format, uint8 bufferindex)
|
||||
case CommonFormat::NONE:
|
||||
break;
|
||||
case CommonFormat::XYf:
|
||||
set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
|
||||
set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
|
||||
break;
|
||||
case CommonFormat::XYZf:
|
||||
set(ATTRIB_POS, DATA_FLOAT, 3, 0, bufferindex);
|
||||
set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC3, 0, bufferindex);
|
||||
break;
|
||||
case CommonFormat::RGBAub:
|
||||
set(ATTRIB_COLOR, DATA_UNORM8, 4, 0, bufferindex);
|
||||
set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, 0, bufferindex);
|
||||
break;
|
||||
case CommonFormat::STf_RGBAub:
|
||||
set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, 0, bufferindex);
|
||||
set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 2), bufferindex);
|
||||
set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
|
||||
set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 2), bufferindex);
|
||||
break;
|
||||
case CommonFormat::STPf_RGBAub:
|
||||
set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, 0, bufferindex);
|
||||
set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 3), bufferindex);
|
||||
set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC3, 0, bufferindex);
|
||||
set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 3), bufferindex);
|
||||
break;
|
||||
case CommonFormat::XYf_STf:
|
||||
set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
|
||||
set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, uint16(sizeof(float) * 2), bufferindex);
|
||||
set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
|
||||
set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC2, uint16(sizeof(float) * 2), bufferindex);
|
||||
break;
|
||||
case CommonFormat::XYf_STPf:
|
||||
set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
|
||||
set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, uint16(sizeof(float) * 2), bufferindex);
|
||||
set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
|
||||
set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC3, uint16(sizeof(float) * 2), bufferindex);
|
||||
break;
|
||||
case CommonFormat::XYf_STf_RGBAub:
|
||||
set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
|
||||
set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, uint16(sizeof(float) * 2), bufferindex);
|
||||
set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 4), bufferindex);
|
||||
set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
|
||||
set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC2, uint16(sizeof(float) * 2), bufferindex);
|
||||
set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 4), bufferindex);
|
||||
break;
|
||||
case CommonFormat::XYf_STus_RGBAub:
|
||||
set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
|
||||
set(ATTRIB_TEXCOORD, DATA_UNORM16, 2, uint16(sizeof(float) * 2), bufferindex);
|
||||
set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 2 + sizeof(uint16) * 2), bufferindex);
|
||||
set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
|
||||
set(ATTRIB_TEXCOORD, DATAFORMAT_UNORM16_VEC2, uint16(sizeof(float) * 2), bufferindex);
|
||||
set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 2 + sizeof(uint16) * 2), bufferindex);
|
||||
break;
|
||||
case CommonFormat::XYf_STPf_RGBAub:
|
||||
set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
|
||||
set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, uint16(sizeof(float) * 2), bufferindex);
|
||||
set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 5), bufferindex);
|
||||
set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
|
||||
set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC3, uint16(sizeof(float) * 2), bufferindex);
|
||||
set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 5), bufferindex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static StringMap<BuiltinVertexAttribute, ATTRIB_MAX_ENUM>::Entry attribNameEntries[] =
|
||||
STRINGMAP_BEGIN(BuiltinVertexAttribute, ATTRIB_MAX_ENUM, attribName)
|
||||
{
|
||||
{ "VertexPosition", ATTRIB_POS },
|
||||
{ "VertexTexCoord", ATTRIB_TEXCOORD },
|
||||
{ "VertexColor", ATTRIB_COLOR },
|
||||
};
|
||||
}
|
||||
STRINGMAP_END(BuiltinVertexAttribute, ATTRIB_MAX_ENUM, attribName)
|
||||
|
||||
static StringMap<BuiltinVertexAttribute, ATTRIB_MAX_ENUM> attribNames(attribNameEntries, sizeof(attribNameEntries));
|
||||
const char *getConstant(BuiltinVertexAttribute attrib)
|
||||
{
|
||||
const char *name = nullptr;
|
||||
getConstant(attrib, name);
|
||||
return name;
|
||||
}
|
||||
|
||||
static StringMap<IndexDataType, INDEX_MAX_ENUM>::Entry indexTypeEntries[] =
|
||||
STRINGMAP_BEGIN(BufferType, BUFFERTYPE_MAX_ENUM, bufferTypeName)
|
||||
{
|
||||
{ "vertex", BUFFERTYPE_VERTEX },
|
||||
{ "index", BUFFERTYPE_INDEX },
|
||||
{ "texel", BUFFERTYPE_TEXEL },
|
||||
}
|
||||
STRINGMAP_END(BufferType, BUFFERTYPE_MAX_ENUM, bufferTypeName)
|
||||
|
||||
STRINGMAP_BEGIN(IndexDataType, INDEX_MAX_ENUM, indexType)
|
||||
{
|
||||
{ "uint16", INDEX_UINT16 },
|
||||
{ "uint32", INDEX_UINT32 },
|
||||
};
|
||||
}
|
||||
STRINGMAP_END(IndexDataType, INDEX_MAX_ENUM, indexType)
|
||||
|
||||
static StringMap<IndexDataType, INDEX_MAX_ENUM> indexTypes(indexTypeEntries, sizeof(indexTypeEntries));
|
||||
|
||||
static StringMap<Usage, USAGE_MAX_ENUM>::Entry usageEntries[] =
|
||||
STRINGMAP_BEGIN(BufferUsage, BUFFERUSAGE_MAX_ENUM, bufferUsage)
|
||||
{
|
||||
{ "stream", USAGE_STREAM },
|
||||
{ "dynamic", USAGE_DYNAMIC },
|
||||
{ "static", USAGE_STATIC },
|
||||
};
|
||||
{ "stream", BUFFERUSAGE_STREAM },
|
||||
{ "dynamic", BUFFERUSAGE_DYNAMIC },
|
||||
{ "static", BUFFERUSAGE_STATIC },
|
||||
}
|
||||
STRINGMAP_END(BufferUsage, BUFFERUSAGE_MAX_ENUM, bufferUsage)
|
||||
|
||||
static StringMap<Usage, USAGE_MAX_ENUM> usages(usageEntries, sizeof(usageEntries));
|
||||
|
||||
static StringMap<PrimitiveType, PRIMITIVE_MAX_ENUM>::Entry primitiveTypeEntries[] =
|
||||
STRINGMAP_BEGIN(PrimitiveType, PRIMITIVE_MAX_ENUM, primitiveType)
|
||||
{
|
||||
{ "fan", PRIMITIVE_TRIANGLE_FAN },
|
||||
{ "strip", PRIMITIVE_TRIANGLE_STRIP },
|
||||
{ "triangles", PRIMITIVE_TRIANGLES },
|
||||
{ "points", PRIMITIVE_POINTS },
|
||||
};
|
||||
}
|
||||
STRINGMAP_END(PrimitiveType, PRIMITIVE_MAX_ENUM, primitiveType)
|
||||
|
||||
static StringMap<PrimitiveType, PRIMITIVE_MAX_ENUM> primitiveTypes(primitiveTypeEntries, sizeof(primitiveTypeEntries));
|
||||
|
||||
static StringMap<AttributeStep, STEP_MAX_ENUM>::Entry attributeStepEntries[] =
|
||||
STRINGMAP_BEGIN(AttributeStep, STEP_MAX_ENUM, attributeStep)
|
||||
{
|
||||
{ "pervertex", STEP_PER_VERTEX },
|
||||
{ "perinstance", STEP_PER_INSTANCE },
|
||||
};
|
||||
}
|
||||
STRINGMAP_END(AttributeStep, STEP_MAX_ENUM, attributeStep)
|
||||
|
||||
static StringMap<AttributeStep, STEP_MAX_ENUM> attributeSteps(attributeStepEntries, sizeof(attributeStepEntries));
|
||||
|
||||
static StringMap<DataType, DATA_MAX_ENUM>::Entry dataTypeEntries[] =
|
||||
STRINGMAP_BEGIN(DataFormat, DATAFORMAT_MAX_ENUM, dataFormat)
|
||||
{
|
||||
{ "snorm8", DATA_SNORM8 },
|
||||
{ "unorm8", DATA_UNORM8 },
|
||||
{ "int8", DATA_INT8 },
|
||||
{ "uint8", DATA_UINT8 },
|
||||
{ "snorm16", DATA_SNORM16 },
|
||||
{ "unorm16", DATA_UNORM16 },
|
||||
{ "int16", DATA_INT16 },
|
||||
{ "uint16", DATA_UINT16 },
|
||||
{ "int32", DATA_INT32 },
|
||||
{ "uint32", DATA_UINT32 },
|
||||
{ "float", DATA_FLOAT },
|
||||
};
|
||||
{ "float", DATAFORMAT_FLOAT },
|
||||
{ "floatvec2", DATAFORMAT_FLOAT_VEC2 },
|
||||
{ "floatvec3", DATAFORMAT_FLOAT_VEC3 },
|
||||
{ "floatvec4", DATAFORMAT_FLOAT_VEC4 },
|
||||
|
||||
static StringMap<DataType, DATA_MAX_ENUM> dataTypes(dataTypeEntries, sizeof(dataTypeEntries));
|
||||
{ "floatmat2x2", DATAFORMAT_FLOAT_MAT2X2 },
|
||||
{ "floatmat2x3", DATAFORMAT_FLOAT_MAT2X3 },
|
||||
{ "floatmat2x4", DATAFORMAT_FLOAT_MAT2X4 },
|
||||
|
||||
static StringMap<CullMode, CULL_MAX_ENUM>::Entry cullModeEntries[] =
|
||||
{ "floatmat3x2", DATAFORMAT_FLOAT_MAT3X2 },
|
||||
{ "floatmat3x3", DATAFORMAT_FLOAT_MAT3X3 },
|
||||
{ "floatmat3x4", DATAFORMAT_FLOAT_MAT3X4 },
|
||||
|
||||
{ "floatmat4x2", DATAFORMAT_FLOAT_MAT4X2 },
|
||||
{ "floatmat4x3", DATAFORMAT_FLOAT_MAT4X3 },
|
||||
{ "floatmat4x4", DATAFORMAT_FLOAT_MAT4X4 },
|
||||
|
||||
{ "int32", DATAFORMAT_INT32 },
|
||||
{ "int32vec2", DATAFORMAT_INT32_VEC2 },
|
||||
{ "int32vec3", DATAFORMAT_INT32_VEC3 },
|
||||
{ "int32vec4", DATAFORMAT_INT32_VEC4 },
|
||||
|
||||
{ "uint32", DATAFORMAT_UINT32 },
|
||||
{ "uint32vec2", DATAFORMAT_UINT32_VEC2 },
|
||||
{ "uint32vec3", DATAFORMAT_UINT32_VEC3 },
|
||||
{ "uint32vec4", DATAFORMAT_UINT32_VEC4 },
|
||||
|
||||
{ "snorm8vec4", DATAFORMAT_SNORM8_VEC4 },
|
||||
{ "unorm8vec4", DATAFORMAT_UNORM8_VEC4 },
|
||||
{ "int8vec4", DATAFORMAT_INT8_VEC4 },
|
||||
{ "uint8vec4", DATAFORMAT_UINT8_VEC4 },
|
||||
|
||||
{ "snorm16vec2", DATAFORMAT_SNORM16_VEC2 },
|
||||
{ "snorm16vec4", DATAFORMAT_SNORM16_VEC4 },
|
||||
|
||||
{ "unorm16vec2", DATAFORMAT_UNORM16_VEC2 },
|
||||
{ "unorm16vec4", DATAFORMAT_UNORM16_VEC4 },
|
||||
|
||||
{ "int16vec2", DATAFORMAT_INT16_VEC2 },
|
||||
{ "int16vec4", DATAFORMAT_INT16_VEC4 },
|
||||
|
||||
{ "uint16", DATAFORMAT_UINT16 },
|
||||
{ "uint16vec2", DATAFORMAT_UINT16_VEC2 },
|
||||
{ "uint16vec4", DATAFORMAT_UINT16_VEC4 },
|
||||
|
||||
{ "bool", DATAFORMAT_BOOL },
|
||||
{ "boolvec2", DATAFORMAT_BOOL_VEC2 },
|
||||
{ "boolvec3", DATAFORMAT_BOOL_VEC3 },
|
||||
{ "boolvec4", DATAFORMAT_BOOL_VEC4 },
|
||||
}
|
||||
STRINGMAP_END(DataFormat, DATAFORMAT_MAX_ENUM, dataFormat)
|
||||
|
||||
STRINGMAP_BEGIN(DataBaseType, DATA_BASETYPE_MAX_ENUM, dataBaseType)
|
||||
{
|
||||
{ "float", DATA_BASETYPE_FLOAT },
|
||||
{ "int", DATA_BASETYPE_INT },
|
||||
{ "uint", DATA_BASETYPE_UINT },
|
||||
{ "snorm", DATA_BASETYPE_SNORM },
|
||||
{ "unorm", DATA_BASETYPE_UNORM },
|
||||
{ "bool", DATA_BASETYPE_BOOL },
|
||||
}
|
||||
STRINGMAP_END(DataBaseType, DATA_BASETYPE_MAX_ENUM, dataBaseType)
|
||||
|
||||
STRINGMAP_BEGIN(CullMode, CULL_MAX_ENUM, cullMode)
|
||||
{
|
||||
{ "none", CULL_NONE },
|
||||
{ "back", CULL_BACK },
|
||||
{ "front", CULL_FRONT },
|
||||
};
|
||||
}
|
||||
STRINGMAP_END(CullMode, CULL_MAX_ENUM, cullMode)
|
||||
|
||||
static StringMap<CullMode, CULL_MAX_ENUM> cullModes(cullModeEntries, sizeof(cullModeEntries));
|
||||
|
||||
static StringMap<Winding, WINDING_MAX_ENUM>::Entry windingEntries[] =
|
||||
STRINGMAP_BEGIN(Winding, WINDING_MAX_ENUM, winding)
|
||||
{
|
||||
{ "cw", WINDING_CW },
|
||||
{ "ccw", WINDING_CCW },
|
||||
};
|
||||
|
||||
static StringMap<Winding, WINDING_MAX_ENUM> windings(windingEntries, sizeof(windingEntries));
|
||||
|
||||
bool getConstant(const char *in, BuiltinVertexAttribute &out)
|
||||
{
|
||||
return attribNames.find(in, out);
|
||||
}
|
||||
STRINGMAP_END(Winding, WINDING_MAX_ENUM, winding)
|
||||
|
||||
bool getConstant(BuiltinVertexAttribute in, const char *&out)
|
||||
{
|
||||
return attribNames.find(in, out);
|
||||
}
|
||||
|
||||
bool getConstant(const char *in, IndexDataType &out)
|
||||
{
|
||||
return indexTypes.find(in, out);
|
||||
}
|
||||
|
||||
bool getConstant(IndexDataType in, const char *&out)
|
||||
{
|
||||
return indexTypes.find(in, out);
|
||||
}
|
||||
|
||||
std::vector<std::string> getConstants(IndexDataType)
|
||||
{
|
||||
return indexTypes.getNames();
|
||||
}
|
||||
|
||||
bool getConstant(const char *in, Usage &out)
|
||||
{
|
||||
return usages.find(in, out);
|
||||
}
|
||||
|
||||
bool getConstant(Usage in, const char *&out)
|
||||
{
|
||||
return usages.find(in, out);
|
||||
}
|
||||
|
||||
std::vector<std::string> getConstants(Usage)
|
||||
{
|
||||
return usages.getNames();
|
||||
}
|
||||
|
||||
bool getConstant(const char *in, PrimitiveType &out)
|
||||
{
|
||||
return primitiveTypes.find(in, out);
|
||||
}
|
||||
|
||||
bool getConstant(PrimitiveType in, const char *&out)
|
||||
{
|
||||
return primitiveTypes.find(in, out);
|
||||
}
|
||||
|
||||
std::vector<std::string> getConstants(PrimitiveType)
|
||||
{
|
||||
return primitiveTypes.getNames();
|
||||
}
|
||||
|
||||
bool getConstant(const char *in, AttributeStep &out)
|
||||
{
|
||||
return attributeSteps.find(in, out);
|
||||
}
|
||||
|
||||
bool getConstant(AttributeStep in, const char *&out)
|
||||
{
|
||||
return attributeSteps.find(in, out);
|
||||
}
|
||||
|
||||
std::vector<std::string> getConstants(AttributeStep)
|
||||
{
|
||||
return attributeSteps.getNames();
|
||||
}
|
||||
|
||||
bool getConstant(const char *in, DataType &out)
|
||||
{
|
||||
return dataTypes.find(in, out);
|
||||
}
|
||||
|
||||
bool getConstant(DataType in, const char *&out)
|
||||
{
|
||||
return dataTypes.find(in, out);
|
||||
}
|
||||
|
||||
std::vector<std::string> getConstants(DataType)
|
||||
{
|
||||
return dataTypes.getNames();
|
||||
}
|
||||
|
||||
bool getConstant(const char *in, CullMode &out)
|
||||
{
|
||||
return cullModes.find(in, out);
|
||||
}
|
||||
|
||||
bool getConstant(CullMode in, const char *&out)
|
||||
{
|
||||
return cullModes.find(in, out);
|
||||
}
|
||||
|
||||
std::vector<std::string> getConstants(CullMode)
|
||||
{
|
||||
return cullModes.getNames();
|
||||
}
|
||||
|
||||
bool getConstant(const char *in, Winding &out)
|
||||
{
|
||||
return windings.find(in, out);
|
||||
}
|
||||
|
||||
bool getConstant(Winding in, const char *&out)
|
||||
{
|
||||
return windings.find(in, out);
|
||||
}
|
||||
|
||||
std::vector<std::string> getConstants(Winding)
|
||||
{
|
||||
return windings.getNames();
|
||||
}
|
||||
|
||||
} // vertex
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
+117
-80
@@ -23,6 +23,7 @@
|
||||
// LOVE
|
||||
#include "common/int.h"
|
||||
#include "common/Color.h"
|
||||
#include "common/StringMap.h"
|
||||
|
||||
// C
|
||||
#include <stddef.h>
|
||||
@@ -46,7 +47,7 @@ enum BuiltinVertexAttribute
|
||||
ATTRIB_MAX_ENUM
|
||||
};
|
||||
|
||||
enum BuiltinVertexAttributeFlag
|
||||
enum BuiltinVertexAttributeFlags
|
||||
{
|
||||
ATTRIBFLAG_POS = 1 << ATTRIB_POS,
|
||||
ATTRIBFLAG_TEXCOORD = 1 << ATTRIB_TEXCOORD,
|
||||
@@ -55,10 +56,10 @@ enum BuiltinVertexAttributeFlag
|
||||
|
||||
enum BufferType
|
||||
{
|
||||
BUFFER_VERTEX = 0,
|
||||
BUFFER_INDEX,
|
||||
BUFFER_UNIFORM,
|
||||
BUFFER_MAX_ENUM
|
||||
BUFFERTYPE_VERTEX = 0,
|
||||
BUFFERTYPE_INDEX,
|
||||
BUFFERTYPE_TEXEL,
|
||||
BUFFERTYPE_MAX_ENUM
|
||||
};
|
||||
|
||||
enum IndexDataType
|
||||
@@ -93,36 +94,81 @@ enum CullMode
|
||||
CULL_MAX_ENUM
|
||||
};
|
||||
|
||||
namespace vertex
|
||||
// The expected usage pattern of buffer data.
|
||||
enum BufferUsage
|
||||
{
|
||||
|
||||
// The expected usage pattern of vertex data.
|
||||
enum Usage
|
||||
{
|
||||
USAGE_STREAM,
|
||||
USAGE_DYNAMIC,
|
||||
USAGE_STATIC,
|
||||
USAGE_MAX_ENUM
|
||||
BUFFERUSAGE_STREAM,
|
||||
BUFFERUSAGE_DYNAMIC,
|
||||
BUFFERUSAGE_STATIC,
|
||||
BUFFERUSAGE_MAX_ENUM
|
||||
};
|
||||
|
||||
enum DataType
|
||||
// Value types used when interfacing with the GPU (vertex and shader data).
|
||||
// The order of this enum affects the dataFormatInfo array.
|
||||
enum DataFormat
|
||||
{
|
||||
DATA_SNORM8,
|
||||
DATA_UNORM8,
|
||||
DATA_INT8,
|
||||
DATA_UINT8,
|
||||
DATAFORMAT_FLOAT,
|
||||
DATAFORMAT_FLOAT_VEC2,
|
||||
DATAFORMAT_FLOAT_VEC3,
|
||||
DATAFORMAT_FLOAT_VEC4,
|
||||
|
||||
DATA_SNORM16,
|
||||
DATA_UNORM16,
|
||||
DATA_INT16,
|
||||
DATA_UINT16,
|
||||
DATAFORMAT_FLOAT_MAT2X2,
|
||||
DATAFORMAT_FLOAT_MAT2X3,
|
||||
DATAFORMAT_FLOAT_MAT2X4,
|
||||
|
||||
DATA_INT32,
|
||||
DATA_UINT32,
|
||||
DATAFORMAT_FLOAT_MAT3X2,
|
||||
DATAFORMAT_FLOAT_MAT3X3,
|
||||
DATAFORMAT_FLOAT_MAT3X4,
|
||||
|
||||
DATA_FLOAT,
|
||||
DATAFORMAT_FLOAT_MAT4X2,
|
||||
DATAFORMAT_FLOAT_MAT4X3,
|
||||
DATAFORMAT_FLOAT_MAT4X4,
|
||||
|
||||
DATA_MAX_ENUM
|
||||
DATAFORMAT_INT32,
|
||||
DATAFORMAT_INT32_VEC2,
|
||||
DATAFORMAT_INT32_VEC3,
|
||||
DATAFORMAT_INT32_VEC4,
|
||||
|
||||
DATAFORMAT_UINT32,
|
||||
DATAFORMAT_UINT32_VEC2,
|
||||
DATAFORMAT_UINT32_VEC3,
|
||||
DATAFORMAT_UINT32_VEC4,
|
||||
|
||||
DATAFORMAT_SNORM8_VEC4,
|
||||
DATAFORMAT_UNORM8_VEC4,
|
||||
DATAFORMAT_INT8_VEC4,
|
||||
DATAFORMAT_UINT8_VEC4,
|
||||
|
||||
DATAFORMAT_SNORM16_VEC2,
|
||||
DATAFORMAT_SNORM16_VEC4,
|
||||
|
||||
DATAFORMAT_UNORM16_VEC2,
|
||||
DATAFORMAT_UNORM16_VEC4,
|
||||
|
||||
DATAFORMAT_INT16_VEC2,
|
||||
DATAFORMAT_INT16_VEC4,
|
||||
|
||||
DATAFORMAT_UINT16,
|
||||
DATAFORMAT_UINT16_VEC2,
|
||||
DATAFORMAT_UINT16_VEC4,
|
||||
|
||||
DATAFORMAT_BOOL,
|
||||
DATAFORMAT_BOOL_VEC2,
|
||||
DATAFORMAT_BOOL_VEC3,
|
||||
DATAFORMAT_BOOL_VEC4,
|
||||
|
||||
DATAFORMAT_MAX_ENUM
|
||||
};
|
||||
|
||||
enum DataBaseType
|
||||
{
|
||||
DATA_BASETYPE_FLOAT,
|
||||
DATA_BASETYPE_INT,
|
||||
DATA_BASETYPE_UINT,
|
||||
DATA_BASETYPE_SNORM,
|
||||
DATA_BASETYPE_UNORM,
|
||||
DATA_BASETYPE_BOOL,
|
||||
DATA_BASETYPE_MAX_ENUM
|
||||
};
|
||||
|
||||
enum Winding
|
||||
@@ -132,12 +178,12 @@ enum Winding
|
||||
WINDING_MAX_ENUM
|
||||
};
|
||||
|
||||
enum class TriangleIndexMode
|
||||
enum TriangleIndexMode
|
||||
{
|
||||
NONE,
|
||||
STRIP,
|
||||
FAN,
|
||||
QUADS,
|
||||
TRIANGLEINDEX_NONE,
|
||||
TRIANGLEINDEX_STRIP,
|
||||
TRIANGLEINDEX_FAN,
|
||||
TRIANGLEINDEX_QUADS,
|
||||
};
|
||||
|
||||
enum class CommonFormat
|
||||
@@ -155,6 +201,17 @@ enum class CommonFormat
|
||||
XYf_STPf_RGBAub,
|
||||
};
|
||||
|
||||
struct DataFormatInfo
|
||||
{
|
||||
DataBaseType baseType;
|
||||
bool isMatrix;
|
||||
int components;
|
||||
int matrixRows;
|
||||
int matrixColumns;
|
||||
size_t componentSize;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct STf_RGBAub
|
||||
{
|
||||
float s, t;
|
||||
@@ -186,6 +243,8 @@ struct XYf_STf_RGBAub
|
||||
Color32 color;
|
||||
};
|
||||
|
||||
typedef XYf_STf_RGBAub Vertex;
|
||||
|
||||
struct XYf_STus_RGBAub
|
||||
{
|
||||
float x, y;
|
||||
@@ -222,42 +281,41 @@ struct BufferBindings
|
||||
void clear() { useBits = 0; }
|
||||
};
|
||||
|
||||
struct AttributeInfo
|
||||
struct VertexAttributeInfo
|
||||
{
|
||||
DataType type;
|
||||
uint8 components;
|
||||
uint8 bufferIndex;
|
||||
DataFormat format : 8;
|
||||
uint16 offsetFromVertex;
|
||||
};
|
||||
|
||||
struct BufferLayout
|
||||
struct VertexBufferLayout
|
||||
{
|
||||
// Attribute step rate is stored outside this struct as a bitmask.
|
||||
uint16 stride;
|
||||
};
|
||||
|
||||
struct Attributes
|
||||
struct VertexAttributes
|
||||
{
|
||||
static const uint32 MAX = 32;
|
||||
|
||||
uint32 enableBits = 0; // indexed by attribute
|
||||
uint32 instanceBits = 0; // indexed by buffer
|
||||
|
||||
AttributeInfo attribs[MAX];
|
||||
BufferLayout bufferLayouts[BufferBindings::MAX];
|
||||
VertexAttributeInfo attribs[MAX];
|
||||
VertexBufferLayout bufferLayouts[BufferBindings::MAX];
|
||||
|
||||
Attributes() {}
|
||||
Attributes(CommonFormat format, uint8 bufferindex)
|
||||
VertexAttributes() {}
|
||||
VertexAttributes(CommonFormat format, uint8 bufferindex)
|
||||
{
|
||||
setCommonFormat(format, bufferindex);
|
||||
}
|
||||
|
||||
void set(uint32 index, DataType type, uint8 components, uint16 offsetfromvertex, uint8 bufferindex)
|
||||
void set(uint32 index, DataFormat format, uint16 offsetfromvertex, uint8 bufferindex)
|
||||
{
|
||||
enableBits |= (1u << index);
|
||||
|
||||
attribs[index].bufferIndex = bufferindex;
|
||||
attribs[index].type = type;
|
||||
attribs[index].components = components;
|
||||
attribs[index].format = format;
|
||||
attribs[index].offsetFromVertex = offsetfromvertex;
|
||||
}
|
||||
|
||||
@@ -307,51 +365,30 @@ inline CommonFormat getSinglePositionFormat(bool is2D)
|
||||
return is2D ? CommonFormat::XYf : CommonFormat::XYZf;
|
||||
}
|
||||
|
||||
size_t getIndexDataSize(IndexDataType type);
|
||||
size_t getDataTypeSize(DataType datatype);
|
||||
bool isDataTypeInteger(DataType datatype);
|
||||
const DataFormatInfo &getDataFormatInfo(DataFormat format);
|
||||
|
||||
size_t getIndexDataSize(IndexDataType type);
|
||||
IndexDataType getIndexDataTypeFromMax(size_t maxvalue);
|
||||
DataFormat getIndexDataFormat(IndexDataType type);
|
||||
IndexDataType getIndexDataType(DataFormat format);
|
||||
|
||||
int getIndexCount(TriangleIndexMode mode, int vertexCount);
|
||||
|
||||
void fillIndices(TriangleIndexMode mode, uint16 vertexStart, uint16 vertexCount, uint16 *indices);
|
||||
void fillIndices(TriangleIndexMode mode, uint32 vertexStart, uint32 vertexCount, uint32 *indices);
|
||||
|
||||
bool getConstant(const char *in, BuiltinVertexAttribute &out);
|
||||
bool getConstant(BuiltinVertexAttribute in, const char *&out);
|
||||
STRINGMAP_DECLARE(BuiltinVertexAttribute);
|
||||
STRINGMAP_DECLARE(BufferType);
|
||||
STRINGMAP_DECLARE(IndexDataType);
|
||||
STRINGMAP_DECLARE(BufferUsage);
|
||||
STRINGMAP_DECLARE(PrimitiveType);
|
||||
STRINGMAP_DECLARE(AttributeStep);
|
||||
STRINGMAP_DECLARE(DataFormat);
|
||||
STRINGMAP_DECLARE(DataBaseType);
|
||||
STRINGMAP_DECLARE(CullMode);
|
||||
STRINGMAP_DECLARE(Winding);
|
||||
|
||||
bool getConstant(const char *in, IndexDataType &out);
|
||||
bool getConstant(IndexDataType in, const char *&out);
|
||||
std::vector<std::string> getConstants(IndexDataType);
|
||||
|
||||
bool getConstant(const char *in, Usage &out);
|
||||
bool getConstant(Usage in, const char *&out);
|
||||
std::vector<std::string> getConstants(Usage);
|
||||
|
||||
bool getConstant(const char *in, PrimitiveType &out);
|
||||
bool getConstant(PrimitiveType in, const char *&out);
|
||||
std::vector<std::string> getConstants(PrimitiveType);
|
||||
|
||||
bool getConstant(const char *in, AttributeStep &out);
|
||||
bool getConstant(AttributeStep in, const char *&out);
|
||||
std::vector<std::string> getConstants(AttributeStep);
|
||||
|
||||
bool getConstant(const char *in, DataType &out);
|
||||
bool getConstant(DataType in, const char *&out);
|
||||
std::vector<std::string> getConstants(DataType);
|
||||
|
||||
bool getConstant(const char *in, CullMode &out);
|
||||
bool getConstant(CullMode in, const char *&out);
|
||||
std::vector<std::string> getConstants(CullMode);
|
||||
|
||||
bool getConstant(const char *in, Winding &out);
|
||||
bool getConstant(Winding in, const char *&out);
|
||||
std::vector<std::string> getConstants(Winding);
|
||||
|
||||
} // vertex
|
||||
|
||||
typedef vertex::XYf_STf_RGBAub Vertex;
|
||||
const char *getConstant(BuiltinVertexAttribute attrib);
|
||||
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -0,0 +1,474 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 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 "wrap_Buffer.h"
|
||||
#include "Buffer.h"
|
||||
#include "common/Data.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
static const double defaultComponents[] = {0.0, 0.0, 0.0, 1.0};
|
||||
|
||||
template <typename T>
|
||||
static inline size_t writeData(lua_State *L, int startidx, int components, char *data)
|
||||
{
|
||||
auto componentdata = (T *) data;
|
||||
|
||||
for (int i = 0; i < components; i++)
|
||||
componentdata[i] = (T) (luaL_optnumber(L, startidx + i, defaultComponents[i]));
|
||||
|
||||
return sizeof(T) * components;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline size_t writeSNormData(lua_State *L, int startidx, int components, char *data)
|
||||
{
|
||||
auto componentdata = (T *) data;
|
||||
const auto maxval = std::numeric_limits<T>::max();
|
||||
|
||||
for (int i = 0; i < components; i++)
|
||||
componentdata[i] = (T) (luax_optnumberclamped(L, startidx + i, -1.0, 1.0, defaultComponents[i]) * maxval);
|
||||
|
||||
return sizeof(T) * components;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline size_t writeUNormData(lua_State *L, int startidx, int components, char *data)
|
||||
{
|
||||
auto componentdata = (T *) data;
|
||||
const auto maxval = std::numeric_limits<T>::max();
|
||||
|
||||
for (int i = 0; i < components; i++)
|
||||
componentdata[i] = (T) (luax_optnumberclamped01(L, startidx + i, 1.0) * maxval);
|
||||
|
||||
return sizeof(T) * components;
|
||||
}
|
||||
|
||||
void luax_writebufferdata(lua_State *L, int startidx, DataFormat format, char *data)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case DATAFORMAT_FLOAT: writeData<float>(L, startidx, 1, data); break;
|
||||
case DATAFORMAT_FLOAT_VEC2: writeData<float>(L, startidx, 2, data); break;
|
||||
case DATAFORMAT_FLOAT_VEC3: writeData<float>(L, startidx, 3, data); break;
|
||||
case DATAFORMAT_FLOAT_VEC4: writeData<float>(L, startidx, 4, data); break;
|
||||
|
||||
case DATAFORMAT_INT32: writeData<int32>(L, startidx, 1, data); break;
|
||||
case DATAFORMAT_INT32_VEC2: writeData<int32>(L, startidx, 2, data); break;
|
||||
case DATAFORMAT_INT32_VEC3: writeData<int32>(L, startidx, 3, data); break;
|
||||
case DATAFORMAT_INT32_VEC4: writeData<int32>(L, startidx, 4, data); break;
|
||||
|
||||
case DATAFORMAT_UINT32: writeData<uint32>(L, startidx, 1, data); break;
|
||||
case DATAFORMAT_UINT32_VEC2: writeData<uint32>(L, startidx, 2, data); break;
|
||||
case DATAFORMAT_UINT32_VEC3: writeData<uint32>(L, startidx, 3, data); break;
|
||||
case DATAFORMAT_UINT32_VEC4: writeData<uint32>(L, startidx, 4, data); break;
|
||||
|
||||
case DATAFORMAT_SNORM8_VEC4: writeSNormData<int8>(L, startidx, 4, data); break;
|
||||
case DATAFORMAT_UNORM8_VEC4: writeUNormData<uint8>(L, startidx, 4, data); break;
|
||||
case DATAFORMAT_INT8_VEC4: writeData<int8>(L, startidx, 4, data); break;
|
||||
case DATAFORMAT_UINT8_VEC4: writeData<uint8>(L, startidx, 4, data); break;
|
||||
|
||||
case DATAFORMAT_SNORM16_VEC2: writeSNormData<int16>(L, startidx, 2, data); break;
|
||||
case DATAFORMAT_SNORM16_VEC4: writeSNormData<int16>(L, startidx, 4, data); break;
|
||||
|
||||
case DATAFORMAT_UNORM16_VEC2: writeUNormData<uint16>(L, startidx, 2, data); break;
|
||||
case DATAFORMAT_UNORM16_VEC4: writeUNormData<uint16>(L, startidx, 4, data); break;
|
||||
|
||||
case DATAFORMAT_INT16_VEC2: writeData<int16>(L, startidx, 2, data); break;
|
||||
case DATAFORMAT_INT16_VEC4: writeData<int16>(L, startidx, 4, data); break;
|
||||
|
||||
case DATAFORMAT_UINT16: writeData<uint16>(L, startidx, 1, data); break;
|
||||
case DATAFORMAT_UINT16_VEC2: writeData<uint16>(L, startidx, 2, data); break;
|
||||
case DATAFORMAT_UINT16_VEC4: writeData<uint16>(L, startidx, 4, data); break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline size_t readData(lua_State *L, int components, const char *data)
|
||||
{
|
||||
const auto componentdata = (const T *) data;
|
||||
|
||||
for (int i = 0; i < components; i++)
|
||||
lua_pushnumber(L, (lua_Number) componentdata[i]);
|
||||
|
||||
return sizeof(T) * components;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline size_t readSNormData(lua_State *L, int components, const char *data)
|
||||
{
|
||||
const auto componentdata = (const T *) data;
|
||||
const auto maxval = std::numeric_limits<T>::max();
|
||||
|
||||
for (int i = 0; i < components; i++)
|
||||
lua_pushnumber(L, std::max(-1.0, (lua_Number) componentdata[i] / (lua_Number)maxval));
|
||||
|
||||
return sizeof(T) * components;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline size_t readUNormData(lua_State *L, int components, const char *data)
|
||||
{
|
||||
const auto componentdata = (const T *) data;
|
||||
const auto maxval = std::numeric_limits<T>::max();
|
||||
|
||||
for (int i = 0; i < components; i++)
|
||||
lua_pushnumber(L, (lua_Number) componentdata[i] / (lua_Number)maxval);
|
||||
|
||||
return sizeof(T) * components;
|
||||
}
|
||||
|
||||
void luax_readbufferdata(lua_State *L, DataFormat format, const char *data)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case DATAFORMAT_FLOAT: readData<float>(L, 1, data); break;
|
||||
case DATAFORMAT_FLOAT_VEC2: readData<float>(L, 2, data); break;
|
||||
case DATAFORMAT_FLOAT_VEC3: readData<float>(L, 3, data); break;
|
||||
case DATAFORMAT_FLOAT_VEC4: readData<float>(L, 4, data); break;
|
||||
|
||||
case DATAFORMAT_INT32: readData<int32>(L, 1, data); break;
|
||||
case DATAFORMAT_INT32_VEC2: readData<int32>(L, 2, data); break;
|
||||
case DATAFORMAT_INT32_VEC3: readData<int32>(L, 3, data); break;
|
||||
case DATAFORMAT_INT32_VEC4: readData<int32>(L, 4, data); break;
|
||||
|
||||
case DATAFORMAT_UINT32: readData<uint32>(L, 1, data); break;
|
||||
case DATAFORMAT_UINT32_VEC2: readData<uint32>(L, 2, data); break;
|
||||
case DATAFORMAT_UINT32_VEC3: readData<uint32>(L, 3, data); break;
|
||||
case DATAFORMAT_UINT32_VEC4: readData<uint32>(L, 4, data); break;
|
||||
|
||||
case DATAFORMAT_SNORM8_VEC4: readSNormData<int8>(L, 4, data); break;
|
||||
case DATAFORMAT_UNORM8_VEC4: readUNormData<uint8>(L, 4, data); break;
|
||||
case DATAFORMAT_INT8_VEC4: readData<int8>(L, 4, data); break;
|
||||
case DATAFORMAT_UINT8_VEC4: readData<uint8>(L, 4, data); break;
|
||||
|
||||
case DATAFORMAT_SNORM16_VEC2: readSNormData<int16>(L, 2, data); break;
|
||||
case DATAFORMAT_SNORM16_VEC4: readSNormData<int16>(L, 4, data); break;
|
||||
|
||||
case DATAFORMAT_UNORM16_VEC2: readUNormData<uint16>(L, 2, data); break;
|
||||
case DATAFORMAT_UNORM16_VEC4: readUNormData<uint16>(L, 4, data); break;
|
||||
|
||||
case DATAFORMAT_INT16_VEC2: readData<int16>(L, 2, data); break;
|
||||
case DATAFORMAT_INT16_VEC4: readData<int16>(L, 4, data); break;
|
||||
|
||||
case DATAFORMAT_UINT16: readData<uint16>(L, 1, data); break;
|
||||
case DATAFORMAT_UINT16_VEC2: readData<uint16>(L, 2, data); break;
|
||||
case DATAFORMAT_UINT16_VEC4: readData<uint16>(L, 4, data); break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
Buffer *luax_checkbuffer(lua_State *L, int idx)
|
||||
{
|
||||
return luax_checktype<Buffer>(L, idx);
|
||||
}
|
||||
|
||||
static int w_Buffer_flush(lua_State *L)
|
||||
{
|
||||
Buffer *t = luax_checkbuffer(L, 1);
|
||||
t->unmap();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int w_Buffer_setArrayData(lua_State *L)
|
||||
{
|
||||
Buffer *t = luax_checkbuffer(L, 1);
|
||||
|
||||
int startindex = (int) luaL_optnumber(L, 3, 1) - 1;
|
||||
|
||||
int count = -1;
|
||||
if (!lua_isnoneornil(L, 4))
|
||||
{
|
||||
count = (int) luaL_checknumber(L, 4);
|
||||
if (count <= 0)
|
||||
return luaL_error(L, "Element count must be greater than 0.");
|
||||
}
|
||||
|
||||
size_t stride = t->getArrayStride();
|
||||
size_t offset = startindex * stride;
|
||||
int arraylength = (int) t->getArrayLength();
|
||||
|
||||
if (startindex >= arraylength || startindex < 0)
|
||||
return luaL_error(L, "Invalid vertex start index (must be between 1 and %d)", arraylength);
|
||||
|
||||
if (luax_istype(L, 2, Data::type))
|
||||
{
|
||||
Data *d = luax_checktype<Data>(L, 2);
|
||||
|
||||
count = count >= 0 ? count : (arraylength - startindex);
|
||||
if (startindex + count > arraylength)
|
||||
return luaL_error(L, "Too many array elements (expected at most %d, got %d)", arraylength - startindex, count);
|
||||
|
||||
size_t datasize = std::min(d->getSize(), count * stride);
|
||||
char *bytedata = (char *) t->map() + offset;
|
||||
|
||||
memcpy(bytedata, d->getData(), datasize);
|
||||
|
||||
t->setMappedRangeModified(offset, datasize);
|
||||
t->unmap();
|
||||
return 0;
|
||||
}
|
||||
|
||||
const std::vector<Buffer::DataMember> &members = t->getDataMembers();
|
||||
|
||||
int ncomponents = 0;
|
||||
for (const Buffer::DataMember &member : members)
|
||||
ncomponents += member.info.components;
|
||||
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
int tablelen = (int) luax_objlen(L, 2);
|
||||
|
||||
lua_rawgeti(L, 2, 1);
|
||||
bool tableoftables = lua_istable(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
if (!tableoftables)
|
||||
{
|
||||
if (tablelen % ncomponents != 0)
|
||||
return luaL_error(L, "Array length in flat array variant of Buffer:setArrayData must be a multiple of the total number of components (%d)", ncomponents);
|
||||
tablelen /= ncomponents;
|
||||
}
|
||||
|
||||
count = count >= 0 ? std::min(count, tablelen) : tablelen;
|
||||
if (startindex + count > arraylength)
|
||||
return luaL_error(L, "Too many array elements (expected at most %d, got %d)", arraylength - startindex, count);
|
||||
|
||||
char *data = (char *) t->map() + offset;
|
||||
|
||||
if (tableoftables)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// get arraydata[index]
|
||||
lua_rawgeti(L, 2, i + 1);
|
||||
luaL_checktype(L, -1, LUA_TTABLE);
|
||||
|
||||
// get arraydata[index][j]
|
||||
for (int j = 1; j <= ncomponents; j++)
|
||||
lua_rawgeti(L, -j, j);
|
||||
|
||||
int idx = -ncomponents;
|
||||
|
||||
for (const Buffer::DataMember &member : members)
|
||||
{
|
||||
luax_writebufferdata(L, idx, member.decl.format, data + member.offset);
|
||||
idx += member.info.components;
|
||||
}
|
||||
|
||||
lua_pop(L, ncomponents + 1);
|
||||
data += stride;
|
||||
}
|
||||
}
|
||||
else // Flat array
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// get arraydata[arrayindex * ncomponents + componentindex]
|
||||
for (int componentindex = 1; componentindex <= ncomponents; componentindex++)
|
||||
lua_rawgeti(L, 2, i * ncomponents + componentindex);
|
||||
|
||||
int idx = -ncomponents;
|
||||
|
||||
for (const Buffer::DataMember &member : members)
|
||||
{
|
||||
luax_writebufferdata(L, idx, member.decl.format, data + member.offset);
|
||||
idx += member.info.components;
|
||||
}
|
||||
|
||||
lua_pop(L, ncomponents);
|
||||
data += stride;
|
||||
}
|
||||
}
|
||||
|
||||
t->setMappedRangeModified(offset, count * stride);
|
||||
t->unmap();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int w_Buffer_setElement(lua_State *L)
|
||||
{
|
||||
Buffer *t = luax_checkbuffer(L, 1);
|
||||
|
||||
size_t index = (size_t) (luaL_checkinteger(L, 2) - 1);
|
||||
if (index >= t->getArrayLength())
|
||||
return luaL_error(L, "Invalid Buffer element index: %d", (int) index + 1);
|
||||
|
||||
size_t stride = t->getArrayStride();
|
||||
size_t offset = index * stride;
|
||||
char *data = (char *) t->map() + offset;
|
||||
const auto &members = t->getDataMembers();
|
||||
|
||||
bool istable = lua_istable(L, 3);
|
||||
int idx = istable ? 1 : 3;
|
||||
|
||||
if (istable)
|
||||
{
|
||||
for (const Buffer::DataMember &member : members)
|
||||
{
|
||||
int components = member.info.components;
|
||||
|
||||
for (int i = idx; i < idx + components; i++)
|
||||
lua_rawgeti(L, 3, i);
|
||||
|
||||
luax_writebufferdata(L, -components, member.decl.format, data + member.offset);
|
||||
|
||||
idx += components;
|
||||
lua_pop(L, components);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const Buffer::DataMember &member : members)
|
||||
{
|
||||
luax_writebufferdata(L, idx, member.decl.format, data + member.offset);
|
||||
idx += member.info.components;
|
||||
}
|
||||
}
|
||||
|
||||
t->setMappedRangeModified(offset, stride);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int w_Buffer_getElement(lua_State *L)
|
||||
{
|
||||
Buffer *t = luax_checkbuffer(L, 1);
|
||||
if ((t->getMapFlags() & Buffer::MAP_READ) == 0)
|
||||
return luaL_error(L, "Buffer:getElement requires the buffer to be created with the 'cpureadable' setting set to true.");
|
||||
|
||||
size_t index = (size_t) (luaL_checkinteger(L, 2) - 1);
|
||||
if (index >= t->getArrayLength())
|
||||
return luaL_error(L, "Invalid Buffer element index: %d", (int) index + 1);
|
||||
|
||||
size_t offset = index * t->getArrayStride();
|
||||
const char *data = (const char *) t->map() + offset;
|
||||
const auto &members = t->getDataMembers();
|
||||
|
||||
int n = 0;
|
||||
|
||||
for (const Buffer::DataMember &member : members)
|
||||
{
|
||||
luax_readbufferdata(L, member.decl.format, data + member.offset);
|
||||
n += member.info.components;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int w_Buffer_getElementCount(lua_State *L)
|
||||
{
|
||||
Buffer *t = luax_checkbuffer(L, 1);
|
||||
lua_pushinteger(L, t->getArrayLength());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int w_Buffer_getElementStride(lua_State *L)
|
||||
{
|
||||
Buffer *t = luax_checkbuffer(L, 1);
|
||||
lua_pushinteger(L, t->getArrayStride());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int w_Buffer_getSize(lua_State *L)
|
||||
{
|
||||
Buffer *t = luax_checkbuffer(L, 1);
|
||||
lua_pushinteger(L, t->getSize());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int w_Buffer_getFormat(lua_State *L)
|
||||
{
|
||||
Buffer *t = luax_checkbuffer(L, 1);
|
||||
const auto &members = t->getDataMembers();
|
||||
|
||||
lua_createtable(L, (int) members.size(), 0);
|
||||
|
||||
for (size_t i = 0; i < members.size(); i++)
|
||||
{
|
||||
const Buffer::DataMember &member = members[i];
|
||||
|
||||
lua_createtable(L, 0, 4);
|
||||
|
||||
lua_pushstring(L, member.decl.name.c_str());
|
||||
lua_setfield(L, -2, "name");
|
||||
|
||||
const char *formatstr = "unknown";
|
||||
getConstant(member.decl.format, formatstr);
|
||||
lua_pushstring(L, formatstr);
|
||||
lua_setfield(L, -2, "format");
|
||||
|
||||
lua_pushinteger(L, member.decl.arrayLength);
|
||||
lua_setfield(L, -2, "arraylength");
|
||||
|
||||
lua_pushinteger(L, member.offset);
|
||||
lua_setfield(L, -2, "offset");
|
||||
|
||||
lua_rawseti(L, -2, i + 1);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int w_Buffer_isBufferType(lua_State *L)
|
||||
{
|
||||
Buffer *t = luax_checkbuffer(L, 1);
|
||||
BufferType buffertype = BUFFERTYPE_MAX_ENUM;
|
||||
const char *typestr = luaL_checkstring(L, 2);
|
||||
if (!getConstant(typestr, buffertype))
|
||||
return luax_enumerror(L, "buffer type", getConstants(buffertype), typestr);
|
||||
luax_pushboolean(L, (t->getTypeFlags() & (1 << buffertype)) != 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int w_Buffer_isCPUReadable(lua_State *L)
|
||||
{
|
||||
Buffer *t = luax_checkbuffer(L, 1);
|
||||
luax_pushboolean(L, (t->getMapFlags() & Buffer::MAP_READ) != 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg w_Buffer_functions[] =
|
||||
{
|
||||
{ "flush", w_Buffer_flush },
|
||||
{ "setArrayData", w_Buffer_setArrayData },
|
||||
{ "setElement", w_Buffer_setElement },
|
||||
{ "getElement", w_Buffer_getElement },
|
||||
{ "getElementCount", w_Buffer_getElementCount },
|
||||
{ "getElementStride", w_Buffer_getElementStride },
|
||||
{ "getSize", w_Buffer_getSize },
|
||||
{ "getFormat", w_Buffer_getFormat },
|
||||
{ "isBufferType", w_Buffer_isBufferType },
|
||||
{ "isCPUReadable", w_Buffer_isCPUReadable },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
extern "C" int luaopen_graphicsbuffer(lua_State *L)
|
||||
{
|
||||
return luax_register_type(L, &Buffer::type, w_Buffer_functions, nullptr);
|
||||
}
|
||||
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 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.
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
// LOVE
|
||||
#include "common/runtime.h"
|
||||
#include "Buffer.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
void luax_writebufferdata(lua_State *L, int startidx, DataFormat format, char *data);
|
||||
void luax_readbufferdata(lua_State *L, DataFormat format, const char *data);
|
||||
|
||||
Buffer *luax_checkbuffer(lua_State *L, int idx);
|
||||
extern "C" int luaopen_graphicsbuffer(lua_State *L);
|
||||
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -45,11 +46,6 @@ static const char graphics_lua[] =
|
||||
#include "wrap_Graphics.lua"
|
||||
;
|
||||
|
||||
// This is in a separate file because VS2013 has a 16KB limit for raw strings..
|
||||
static const char graphics_shader_lua[] =
|
||||
#include "wrap_GraphicsShader.lua"
|
||||
;
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
@@ -748,15 +744,15 @@ static int w__pushNewTexture(lua_State *L, Texture::Slices *slices, const Textur
|
||||
static void luax_checktexturesettings(lua_State *L, int idx, bool opt, bool checkType, bool checkDimensions, OptionalBool forceRenderTarget, Texture::Settings &s, bool &setdpiscale)
|
||||
{
|
||||
setdpiscale = false;
|
||||
if (forceRenderTarget.hasValue)
|
||||
s.renderTarget = forceRenderTarget.value;
|
||||
|
||||
if (opt && lua_isnoneornil(L, idx))
|
||||
return;
|
||||
|
||||
luax_checktablefields<Texture::SettingType>(L, idx, "texture setting name", Texture::getConstant);
|
||||
|
||||
if (forceRenderTarget.hasValue)
|
||||
s.renderTarget = forceRenderTarget.value;
|
||||
else
|
||||
if (!forceRenderTarget.hasValue)
|
||||
s.renderTarget = luax_boolflag(L, idx, Texture::getConstant(Texture::SETTING_RENDER_TARGET), s.renderTarget);
|
||||
|
||||
lua_getfield(L, idx, Texture::getConstant(Texture::SETTING_FORMAT));
|
||||
@@ -1199,25 +1195,25 @@ int w_newVolumeTexture(lua_State *L)
|
||||
|
||||
int w_newImage(lua_State *L)
|
||||
{
|
||||
luax_markdeprecated(L, "love.graphics.newImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newTexture");
|
||||
//luax_markdeprecated(L, "love.graphics.newImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newTexture");
|
||||
return w_newTexture(L);
|
||||
}
|
||||
|
||||
int w_newCubeImage(lua_State *L)
|
||||
{
|
||||
luax_markdeprecated(L, "love.graphics.newCubeImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newCubeTexture");
|
||||
//luax_markdeprecated(L, "love.graphics.newCubeImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newCubeTexture");
|
||||
return w_newCubeTexture(L);
|
||||
}
|
||||
|
||||
int w_newArrayImage(lua_State *L)
|
||||
{
|
||||
luax_markdeprecated(L, "love.graphics.newArrayImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newArrayTexture");
|
||||
//luax_markdeprecated(L, "love.graphics.newArrayImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newArrayTexture");
|
||||
return w_newArrayTexture(L);
|
||||
}
|
||||
|
||||
int w_newVolumeImage(lua_State *L)
|
||||
{
|
||||
luax_markdeprecated(L, "love.graphics.newVolumeImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newVolumeTexture");
|
||||
//luax_markdeprecated(L, "love.graphics.newVolumeImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newVolumeTexture");
|
||||
return w_newVolumeTexture(L);
|
||||
}
|
||||
|
||||
@@ -1329,12 +1325,12 @@ int w_newSpriteBatch(lua_State *L)
|
||||
|
||||
Texture *texture = luax_checktexture(L, 1);
|
||||
int size = (int) luaL_optinteger(L, 2, 1000);
|
||||
vertex::Usage usage = vertex::USAGE_DYNAMIC;
|
||||
BufferUsage usage = BUFFERUSAGE_DYNAMIC;
|
||||
if (lua_gettop(L) > 2)
|
||||
{
|
||||
const char *usagestr = luaL_checkstring(L, 3);
|
||||
if (!vertex::getConstant(usagestr, usage))
|
||||
return luax_enumerror(L, "usage hint", vertex::getConstants(usage), usagestr);
|
||||
if (!getConstant(usagestr, usage))
|
||||
return luax_enumerror(L, "usage hint", getConstants(usage), usagestr);
|
||||
}
|
||||
|
||||
SpriteBatch *t = nullptr;
|
||||
@@ -1366,7 +1362,7 @@ int w_newParticleSystem(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int w_getShaderSource(lua_State *L, int startidx, bool gles, std::string &vertexsource, std::string &pixelsource)
|
||||
static int w_getShaderSource(lua_State *L, int startidx, std::vector<std::string> &stages)
|
||||
{
|
||||
using namespace love::filesystem;
|
||||
|
||||
@@ -1426,61 +1422,23 @@ static int w_getShaderSource(lua_State *L, int startidx, bool gles, std::string
|
||||
if (!(has_arg1 || has_arg2))
|
||||
luaL_checkstring(L, startidx);
|
||||
|
||||
luax_getfunction(L, "graphics", "_shaderCodeToGLSL");
|
||||
|
||||
// push vertexcode and pixelcode strings to the top of the stack
|
||||
lua_pushboolean(L, gles);
|
||||
|
||||
if (has_arg1)
|
||||
lua_pushvalue(L, startidx + 0);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
|
||||
stages.push_back(luax_checkstring(L, startidx + 0));
|
||||
if (has_arg2)
|
||||
lua_pushvalue(L, startidx + 1);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
|
||||
// call effectCodeToGLSL, returned values will be at the top of the stack
|
||||
if (lua_pcall(L, 3, 2, 0) != 0)
|
||||
return luaL_error(L, "%s", lua_tostring(L, -1));
|
||||
|
||||
// vertex shader code
|
||||
if (lua_isstring(L, -2))
|
||||
vertexsource = luax_checkstring(L, -2);
|
||||
else if (has_arg1 && has_arg2)
|
||||
return luaL_error(L, "Could not parse vertex shader code (missing 'position' function?)");
|
||||
|
||||
// pixel shader code
|
||||
if (lua_isstring(L, -1))
|
||||
pixelsource = luax_checkstring(L, -1);
|
||||
else if (has_arg1 && has_arg2)
|
||||
return luaL_error(L, "Could not parse pixel shader code (missing 'effect' function?)");
|
||||
|
||||
if (vertexsource.empty() && pixelsource.empty())
|
||||
{
|
||||
// Original args had source code, but effectCodeToGLSL couldn't translate it
|
||||
for (int i = startidx; i < startidx + 2; i++)
|
||||
{
|
||||
if (lua_isstring(L, i))
|
||||
return luaL_argerror(L, i, "missing 'position' or 'effect' function?");
|
||||
}
|
||||
}
|
||||
stages.push_back(luax_checkstring(L, startidx + 1));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_newShader(lua_State *L)
|
||||
{
|
||||
bool gles = instance()->usesGLSLES();
|
||||
|
||||
std::string vertexsource, pixelsource;
|
||||
w_getShaderSource(L, 1, gles, vertexsource, pixelsource);
|
||||
std::vector<std::string> stages;
|
||||
w_getShaderSource(L, 1, stages);
|
||||
|
||||
bool should_error = false;
|
||||
try
|
||||
{
|
||||
Shader *shader = instance()->newShader(vertexsource, pixelsource);
|
||||
Shader *shader = instance()->newShader(stages);
|
||||
luax_pushtype(L, shader);
|
||||
shader->release();
|
||||
}
|
||||
@@ -1504,14 +1462,14 @@ int w_validateShader(lua_State *L)
|
||||
{
|
||||
bool gles = luax_checkboolean(L, 1);
|
||||
|
||||
std::string vertexsource, pixelsource;
|
||||
w_getShaderSource(L, 2, gles, vertexsource, pixelsource);
|
||||
std::vector<std::string> stages;
|
||||
w_getShaderSource(L, 2, stages);
|
||||
|
||||
bool success = true;
|
||||
std::string err;
|
||||
try
|
||||
{
|
||||
success = instance()->validateShader(gles, vertexsource, pixelsource, err);
|
||||
success = instance()->validateShader(gles, stages, err);
|
||||
}
|
||||
catch (love::Exception &e)
|
||||
{
|
||||
@@ -1530,22 +1488,311 @@ int w_validateShader(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static vertex::Usage luax_optmeshusage(lua_State *L, int idx, vertex::Usage def)
|
||||
static BufferUsage luax_optbufferusage(lua_State *L, int idx, BufferUsage def)
|
||||
{
|
||||
const char *usagestr = lua_isnoneornil(L, idx) ? nullptr : luaL_checkstring(L, idx);
|
||||
|
||||
if (usagestr && !vertex::getConstant(usagestr, def))
|
||||
luax_enumerror(L, "usage hint", vertex::getConstants(def), usagestr);
|
||||
if (usagestr && !getConstant(usagestr, def))
|
||||
luax_enumerror(L, "usage hint", getConstants(def), usagestr);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
static void luax_optbuffersettings(lua_State *L, int idx, Buffer::Settings &settings)
|
||||
{
|
||||
if (lua_isnoneornil(L, idx))
|
||||
return;
|
||||
|
||||
luaL_checktype(L, idx, LUA_TTABLE);
|
||||
|
||||
lua_getfield(L, idx, "usage");
|
||||
settings.usage = luax_optbufferusage(L, -1, settings.usage);
|
||||
lua_pop(L, 1);
|
||||
|
||||
if (luax_boolflag(L, idx, "cpureadable", settings.mapFlags & Buffer::MAP_READ))
|
||||
settings.mapFlags = (Buffer::MapFlags)(settings.mapFlags | Buffer::MAP_READ);
|
||||
else
|
||||
settings.mapFlags = (Buffer::MapFlags)(settings.mapFlags & (~Buffer::MAP_READ));
|
||||
}
|
||||
|
||||
static void luax_checkbufferformat(lua_State *L, int idx, std::vector<Buffer::DataDeclaration> &format)
|
||||
{
|
||||
if (lua_type(L, idx) == LUA_TSTRING)
|
||||
{
|
||||
Buffer::DataDeclaration decl("", DATAFORMAT_MAX_ENUM);
|
||||
const char *formatstr = luaL_checkstring(L, idx);
|
||||
if (!getConstant(formatstr, decl.format))
|
||||
luax_enumerror(L, "data format", getConstants(decl.format), formatstr);
|
||||
format.push_back(decl);
|
||||
return;
|
||||
}
|
||||
|
||||
luaL_checktype(L, idx, LUA_TTABLE);
|
||||
int tablelen = luax_objlen(L, idx);
|
||||
|
||||
for (int i = 1; i <= tablelen; i++)
|
||||
{
|
||||
lua_rawgeti(L, idx, i);
|
||||
luaL_checktype(L, -1, LUA_TTABLE);
|
||||
|
||||
Buffer::DataDeclaration decl("", DATAFORMAT_MAX_ENUM);
|
||||
|
||||
lua_getfield(L, -1, "name");
|
||||
if (!lua_isnoneornil(L, -1))
|
||||
decl.name = luax_checkstring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, -1, "format");
|
||||
if (lua_type(L, -1) != LUA_TSTRING)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "'format' field expected in array element #";
|
||||
ss << i;
|
||||
ss << " of format table";
|
||||
std::string str = ss.str();
|
||||
luaL_argerror(L, idx, str.c_str());
|
||||
}
|
||||
const char *formatstr = luaL_checkstring(L, -1);
|
||||
if (!getConstant(formatstr, decl.format))
|
||||
luax_enumerror(L, "data format", getConstants(decl.format), formatstr);
|
||||
lua_pop(L, 1);
|
||||
|
||||
decl.arrayLength = luax_intflag(L, -1, "arraylength", 0);
|
||||
|
||||
format.push_back(decl);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static Buffer *luax_newbuffer(lua_State *L, int idx, const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format)
|
||||
{
|
||||
size_t arraylength = 0;
|
||||
size_t bytesize = 0;
|
||||
Data *data = nullptr;
|
||||
const void *initialdata = nullptr;
|
||||
|
||||
int ncomponents = 0;
|
||||
for (const Buffer::DataDeclaration &decl : format)
|
||||
ncomponents += getDataFormatInfo(decl.format).components;
|
||||
|
||||
if (luax_istype(L, idx, Data::type))
|
||||
{
|
||||
data = luax_checktype<Data>(L, idx);
|
||||
initialdata = data->getData();
|
||||
bytesize = data->getSize();
|
||||
}
|
||||
|
||||
bool tableoftables = false;
|
||||
|
||||
if (lua_istable(L, idx))
|
||||
{
|
||||
arraylength = luax_objlen(L, idx);
|
||||
|
||||
lua_rawgeti(L, idx, 1);
|
||||
tableoftables = lua_istable(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
if (!tableoftables)
|
||||
{
|
||||
if (arraylength % ncomponents != 0)
|
||||
luaL_error(L, "Array length in flat array variant of newBuffer must be a multiple of the total number of components (%d)", ncomponents);
|
||||
arraylength /= ncomponents;
|
||||
}
|
||||
}
|
||||
else if (data == nullptr)
|
||||
{
|
||||
lua_Integer len = luaL_checkinteger(L, idx);
|
||||
if (len <= 0)
|
||||
luaL_argerror(L, idx, "number of elements must be greater than 0");
|
||||
arraylength = (size_t) len;
|
||||
}
|
||||
|
||||
Buffer *b = nullptr;
|
||||
luax_catchexcept(L, [&] { b = instance()->newBuffer(settings, format, initialdata, bytesize, arraylength); });
|
||||
|
||||
if (lua_istable(L, idx))
|
||||
{
|
||||
Buffer::Mapper mapper(*b);
|
||||
char *data = (char *) mapper.data;
|
||||
const auto &members = b->getDataMembers();
|
||||
size_t stride = b->getArrayStride();
|
||||
|
||||
if (tableoftables)
|
||||
{
|
||||
for (size_t i = 0; i < arraylength; i++)
|
||||
{
|
||||
// get arraydata[index]
|
||||
lua_rawgeti(L, 2, i + 1);
|
||||
luaL_checktype(L, -1, LUA_TTABLE);
|
||||
|
||||
// get arraydata[index][j]
|
||||
for (int j = 1; j <= ncomponents; j++)
|
||||
lua_rawgeti(L, -j, j);
|
||||
|
||||
int idx = -ncomponents;
|
||||
|
||||
for (const Buffer::DataMember &member : members)
|
||||
{
|
||||
luax_writebufferdata(L, idx, member.decl.format, data + member.offset);
|
||||
idx += member.info.components;
|
||||
}
|
||||
|
||||
lua_pop(L, ncomponents + 1);
|
||||
data += stride;
|
||||
}
|
||||
}
|
||||
else // Flat array
|
||||
{
|
||||
for (size_t i = 0; i < arraylength; i++)
|
||||
{
|
||||
// get arraydata[arrayindex * ncomponents + componentindex]
|
||||
for (int componentindex = 1; componentindex <= ncomponents; componentindex++)
|
||||
lua_rawgeti(L, 2, i * ncomponents + componentindex);
|
||||
|
||||
int idx = -ncomponents;
|
||||
|
||||
for (const Buffer::DataMember &member : members)
|
||||
{
|
||||
luax_writebufferdata(L, idx, member.decl.format, data + member.offset);
|
||||
idx += member.info.components;
|
||||
}
|
||||
|
||||
lua_pop(L, ncomponents);
|
||||
data += stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
int w_newBuffer(lua_State *L)
|
||||
{
|
||||
Buffer::Settings settings(0, Buffer::MAP_EXPLICIT_RANGE_MODIFY, BUFFERUSAGE_DYNAMIC);
|
||||
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
|
||||
for (int i = 0; i < BUFFERTYPE_MAX_ENUM; i++)
|
||||
{
|
||||
BufferType buffertype = (BufferType) i;
|
||||
const char *tname = nullptr;
|
||||
if (!getConstant(buffertype, tname))
|
||||
continue;
|
||||
if (luax_boolflag(L, 3, tname, false))
|
||||
settings.typeFlags = (Buffer::TypeFlags)(settings.typeFlags | (1u << i));
|
||||
}
|
||||
|
||||
luax_optbuffersettings(L, 3, settings);
|
||||
|
||||
std::vector<Buffer::DataDeclaration> format;
|
||||
luax_checkbufferformat(L, 1, format);
|
||||
|
||||
Buffer *b = luax_newbuffer(L, 2, settings, format);
|
||||
|
||||
luax_pushtype(L, b);
|
||||
b->release();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_newVertexBuffer(lua_State *L)
|
||||
{
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY, BUFFERUSAGE_DYNAMIC);
|
||||
luax_optbuffersettings(L, 3, settings);
|
||||
|
||||
std::vector<Buffer::DataDeclaration> format;
|
||||
luax_checkbufferformat(L, 1, format);
|
||||
|
||||
Buffer *b = luax_newbuffer(L, 2, settings, format);
|
||||
|
||||
luax_pushtype(L, b);
|
||||
b->release();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_newIndexBuffer(lua_State *L)
|
||||
{
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY, BUFFERUSAGE_DYNAMIC);
|
||||
luax_optbuffersettings(L, 3, settings);
|
||||
|
||||
size_t arraylength = 0;
|
||||
size_t bytesize = 0;
|
||||
DataFormat format = DATAFORMAT_UINT16;
|
||||
Data *data = nullptr;
|
||||
const void *initialdata = nullptr;
|
||||
|
||||
if (luax_istype(L, 1, Data::type))
|
||||
{
|
||||
data = luax_checktype<Data>(L, 1);
|
||||
initialdata = data->getData();
|
||||
bytesize = data->getSize();
|
||||
}
|
||||
|
||||
if (lua_istable(L, 1))
|
||||
{
|
||||
arraylength = luax_objlen(L, 1);
|
||||
|
||||
// Scan array for invalid types and the max value.
|
||||
lua_Integer maxvalue = 0;
|
||||
for (size_t i = 0; i < arraylength; i++)
|
||||
{
|
||||
lua_rawgeti(L, 1, i + 1);
|
||||
lua_Integer v = luaL_checkinteger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
if (v < 0)
|
||||
return luaL_argerror(L, 1, "expected positive integer values in array");
|
||||
else
|
||||
maxvalue = std::max(maxvalue, v);
|
||||
}
|
||||
|
||||
format = getIndexDataFormat(getIndexDataTypeFromMax(maxvalue));
|
||||
}
|
||||
else if (data == nullptr)
|
||||
{
|
||||
lua_Integer len = luaL_checkinteger(L, 1);
|
||||
if (len <= 0)
|
||||
return luaL_argerror(L, 1, "number of elements must be greater than 0");
|
||||
arraylength = (size_t) len;
|
||||
}
|
||||
|
||||
if (data != nullptr || !lua_isnoneornil(L, 2))
|
||||
{
|
||||
const char *formatstr = luaL_checkstring(L, 2);
|
||||
if (!getConstant(formatstr, format))
|
||||
return luax_enumerror(L, "index data format", getConstants(format), formatstr);
|
||||
}
|
||||
|
||||
Buffer *b = nullptr;
|
||||
luax_catchexcept(L, [&] { b = instance()->newBuffer(settings, format, initialdata, bytesize, arraylength); });
|
||||
|
||||
if (lua_istable(L, 1))
|
||||
{
|
||||
Buffer::Mapper mapper(*b);
|
||||
uint16 *u16data = (uint16 *) mapper.data;
|
||||
uint32 *u32data = (uint32 *) mapper.data;
|
||||
|
||||
for (size_t i = 0; i < arraylength; i++)
|
||||
{
|
||||
lua_rawgeti(L, 1, i + 1);
|
||||
lua_Integer v = luaL_checkinteger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
if (format == DATAFORMAT_UINT16)
|
||||
u16data[i] = (uint16) v;
|
||||
else
|
||||
u32data[i] = (uint32) v;
|
||||
}
|
||||
}
|
||||
|
||||
luax_pushtype(L, b);
|
||||
b->release();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static PrimitiveType luax_optmeshdrawmode(lua_State *L, int idx, PrimitiveType def)
|
||||
{
|
||||
const char *modestr = lua_isnoneornil(L, idx) ? nullptr : luaL_checkstring(L, idx);
|
||||
|
||||
if (modestr && !vertex::getConstant(modestr, def))
|
||||
luax_enumerror(L, "mesh draw mode", vertex::getConstants(def), modestr);
|
||||
if (modestr && !getConstant(modestr, def))
|
||||
luax_enumerror(L, "mesh draw mode", getConstants(def), modestr);
|
||||
|
||||
return def;
|
||||
}
|
||||
@@ -1555,7 +1802,9 @@ static Mesh *newStandardMesh(lua_State *L)
|
||||
Mesh *t = nullptr;
|
||||
|
||||
PrimitiveType drawmode = luax_optmeshdrawmode(L, 2, PRIMITIVE_TRIANGLE_FAN);
|
||||
vertex::Usage usage = luax_optmeshusage(L, 3, vertex::USAGE_DYNAMIC);
|
||||
BufferUsage usage = luax_optbufferusage(L, 3, BUFFERUSAGE_DYNAMIC);
|
||||
|
||||
std::vector<Buffer::DataDeclaration> format = Mesh::getDefaultVertexFormat();
|
||||
|
||||
// First argument is a table of standard vertices, or the number of
|
||||
// standard vertices.
|
||||
@@ -1595,12 +1844,12 @@ static Mesh *newStandardMesh(lua_State *L)
|
||||
vertices.push_back(v);
|
||||
}
|
||||
|
||||
luax_catchexcept(L, [&](){ t = instance()->newMesh(vertices, drawmode, usage); });
|
||||
luax_catchexcept(L, [&](){ t = instance()->newMesh(format, vertices.data(), vertices.size() * sizeof(Vertex), drawmode, usage); });
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = (int) luaL_checkinteger(L, 1);
|
||||
luax_catchexcept(L, [&](){ t = instance()->newMesh(count, drawmode, usage); });
|
||||
luax_catchexcept(L, [&](){ t = instance()->newMesh(format, count, drawmode, usage); });
|
||||
}
|
||||
|
||||
return t;
|
||||
@@ -1612,10 +1861,10 @@ static Mesh *newCustomMesh(lua_State *L)
|
||||
|
||||
// First argument is the vertex format, second is a table of vertices or
|
||||
// the number of vertices.
|
||||
std::vector<Mesh::AttribFormat> vertexformat;
|
||||
std::vector<Buffer::DataDeclaration> vertexformat;
|
||||
|
||||
PrimitiveType drawmode = luax_optmeshdrawmode(L, 3, PRIMITIVE_TRIANGLE_FAN);
|
||||
vertex::Usage usage = luax_optmeshusage(L, 4, vertex::USAGE_DYNAMIC);
|
||||
BufferUsage usage = luax_optbufferusage(L, 4, BUFFERUSAGE_DYNAMIC);
|
||||
|
||||
lua_rawgeti(L, 1, 1);
|
||||
if (!lua_istable(L, -1))
|
||||
@@ -1634,27 +1883,53 @@ static Mesh *newCustomMesh(lua_State *L)
|
||||
for (int j = 1; j <= 3; j++)
|
||||
lua_rawgeti(L, -j, j);
|
||||
|
||||
Mesh::AttribFormat format;
|
||||
format.name = luaL_checkstring(L, -3);
|
||||
const char *name = luaL_checkstring(L, -3);
|
||||
|
||||
DataFormat format = DATAFORMAT_MAX_ENUM;
|
||||
const char *tname = luaL_checkstring(L, -2);
|
||||
if (strcmp(tname, "byte") == 0) // Legacy name.
|
||||
format.type = vertex::DATA_UNORM8;
|
||||
else if (!vertex::getConstant(tname, format.type))
|
||||
|
||||
if (!lua_isnoneornil(L, -1))
|
||||
{
|
||||
luax_enumerror(L, "Mesh vertex data type name", vertex::getConstants(format.type), tname);
|
||||
return nullptr;
|
||||
int components = (int) luaL_checkinteger(L, -1);
|
||||
|
||||
// Check deprecated format names.
|
||||
if (strcmp(tname, "byte") == 0 || strcmp(tname, "unorm8") == 0)
|
||||
{
|
||||
if (components == 4)
|
||||
format = DATAFORMAT_UNORM8_VEC4;
|
||||
else
|
||||
luaL_error(L, "Invalid component count (%d) for vertex data type %s", components, tname);
|
||||
}
|
||||
else if (strcmp(tname, "unorm16") == 0)
|
||||
{
|
||||
if (components == 2)
|
||||
format = DATAFORMAT_UNORM16_VEC2;
|
||||
else if (components == 4)
|
||||
format = DATAFORMAT_UNORM16_VEC4;
|
||||
else
|
||||
luaL_error(L, "Invalid component count (%d) for vertex data type %s", components, tname);
|
||||
|
||||
}
|
||||
else if (strcmp(tname, "float") == 0)
|
||||
{
|
||||
if (components == 1)
|
||||
format = DATAFORMAT_FLOAT;
|
||||
else if (components == 2)
|
||||
format = DATAFORMAT_FLOAT_VEC2;
|
||||
else if (components == 3)
|
||||
format = DATAFORMAT_FLOAT_VEC3;
|
||||
else if (components == 4)
|
||||
format = DATAFORMAT_FLOAT_VEC4;
|
||||
else
|
||||
luaL_error(L, "Invalid component count (%d) for vertex data type %s", components, tname);
|
||||
}
|
||||
}
|
||||
|
||||
format.components = (int) luaL_checkinteger(L, -1);
|
||||
if (format.components <= 0 || format.components > 4)
|
||||
{
|
||||
luaL_error(L, "Number of vertex attribute components must be between 1 and 4 (got %d)", format.components);
|
||||
return nullptr;
|
||||
}
|
||||
if (format == DATAFORMAT_MAX_ENUM && !getConstant(tname, format))
|
||||
luax_enumerror(L, "vertex data format", getConstants(format), tname);
|
||||
|
||||
lua_pop(L, 4);
|
||||
vertexformat.push_back(format);
|
||||
vertexformat.emplace_back(name, format);
|
||||
}
|
||||
|
||||
if (lua_isnumber(L, 2))
|
||||
@@ -1679,10 +1954,6 @@ static Mesh *newCustomMesh(lua_State *L)
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
int vertexcomponents = 0;
|
||||
for (const Mesh::AttribFormat &format : vertexformat)
|
||||
vertexcomponents += format.components;
|
||||
|
||||
size_t numvertices = luax_objlen(L, 2);
|
||||
|
||||
luax_catchexcept(L, [&](){ t = instance()->newMesh(vertexformat, numvertices, drawmode, usage); });
|
||||
@@ -1699,19 +1970,19 @@ static Mesh *newCustomMesh(lua_State *L)
|
||||
int n = 0;
|
||||
for (size_t i = 0; i < vertexformat.size(); i++)
|
||||
{
|
||||
int components = vertexformat[i].components;
|
||||
const auto &info = getDataFormatInfo(vertexformat[i].format);
|
||||
|
||||
// get vertices[vertindex][n]
|
||||
for (int c = 0; c < components; c++)
|
||||
for (int c = 0; c < info.components; c++)
|
||||
{
|
||||
n++;
|
||||
lua_rawgeti(L, -(c + 1), n);
|
||||
}
|
||||
|
||||
// Fetch the values from Lua and store them in data buffer.
|
||||
luax_writeAttributeData(L, -components, vertexformat[i].type, components, data);
|
||||
luax_writebufferdata(L, -info.components, vertexformat[i].format, data);
|
||||
|
||||
lua_pop(L, components);
|
||||
lua_pop(L, info.components);
|
||||
|
||||
luax_catchexcept(L,
|
||||
[&](){ t->setVertexAttribute(vertindex, i, data, sizeof(float) * 4); },
|
||||
@@ -2214,8 +2485,8 @@ int w_setMeshCullMode(lua_State *L)
|
||||
const char *str = luaL_checkstring(L, 1);
|
||||
CullMode mode;
|
||||
|
||||
if (!vertex::getConstant(str, mode))
|
||||
return luax_enumerror(L, "cull mode", vertex::getConstants(mode), str);
|
||||
if (!getConstant(str, mode))
|
||||
return luax_enumerror(L, "cull mode", getConstants(mode), str);
|
||||
|
||||
luax_catchexcept(L, [&]() { instance()->setMeshCullMode(mode); });
|
||||
return 0;
|
||||
@@ -2225,7 +2496,7 @@ int w_getMeshCullMode(lua_State *L)
|
||||
{
|
||||
CullMode mode = instance()->getMeshCullMode();
|
||||
const char *str;
|
||||
if (!vertex::getConstant(mode, str))
|
||||
if (!getConstant(mode, str))
|
||||
return luaL_error(L, "Unknown cull mode");
|
||||
lua_pushstring(L, str);
|
||||
return 1;
|
||||
@@ -2234,10 +2505,10 @@ int w_getMeshCullMode(lua_State *L)
|
||||
int w_setFrontFaceWinding(lua_State *L)
|
||||
{
|
||||
const char *str = luaL_checkstring(L, 1);
|
||||
vertex::Winding winding;
|
||||
Winding winding;
|
||||
|
||||
if (!vertex::getConstant(str, winding))
|
||||
return luax_enumerror(L, "vertex winding", vertex::getConstants(winding), str);
|
||||
if (!getConstant(str, winding))
|
||||
return luax_enumerror(L, "vertex winding", getConstants(winding), str);
|
||||
|
||||
luax_catchexcept(L, [&]() { instance()->setFrontFaceWinding(winding); });
|
||||
return 0;
|
||||
@@ -2245,9 +2516,9 @@ int w_setFrontFaceWinding(lua_State *L)
|
||||
|
||||
int w_getFrontFaceWinding(lua_State *L)
|
||||
{
|
||||
vertex::Winding winding = instance()->getFrontFaceWinding();
|
||||
Winding winding = instance()->getFrontFaceWinding();
|
||||
const char *str;
|
||||
if (!vertex::getConstant(winding, str))
|
||||
if (!getConstant(winding, str))
|
||||
return luaL_error(L, "Unknown vertex winding");
|
||||
lua_pushstring(L, str);
|
||||
return 1;
|
||||
@@ -2289,46 +2560,6 @@ int w_getShader(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_setDefaultShaderCode(lua_State *L)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
luaL_checktype(L, i + 1, LUA_TTABLE);
|
||||
|
||||
for (int lang = 0; lang < Shader::LANGUAGE_MAX_ENUM; lang++)
|
||||
{
|
||||
const char *langname;
|
||||
if (!Shader::getConstant((Shader::Language) lang, langname))
|
||||
continue;
|
||||
|
||||
lua_getfield(L, i + 1, langname);
|
||||
|
||||
lua_getfield(L, -1, "vertex");
|
||||
lua_getfield(L, -2, "pixel");
|
||||
lua_getfield(L, -3, "videopixel");
|
||||
lua_getfield(L, -4, "arraypixel");
|
||||
|
||||
std::string vertex = luax_checkstring(L, -4);
|
||||
std::string pixel = luax_checkstring(L, -3);
|
||||
std::string videopixel = luax_checkstring(L, -2);
|
||||
std::string arraypixel = luax_checkstring(L, -1);
|
||||
|
||||
lua_pop(L, 5);
|
||||
|
||||
Graphics::defaultShaderCode[Shader::STANDARD_DEFAULT][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex;
|
||||
Graphics::defaultShaderCode[Shader::STANDARD_DEFAULT][lang][i].source[ShaderStage::STAGE_PIXEL] = pixel;
|
||||
|
||||
Graphics::defaultShaderCode[Shader::STANDARD_VIDEO][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex;
|
||||
Graphics::defaultShaderCode[Shader::STANDARD_VIDEO][lang][i].source[ShaderStage::STAGE_PIXEL] = videopixel;
|
||||
|
||||
Graphics::defaultShaderCode[Shader::STANDARD_ARRAY][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex;
|
||||
Graphics::defaultShaderCode[Shader::STANDARD_ARRAY][lang][i].source[ShaderStage::STAGE_PIXEL] = arraypixel;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_getSupported(lua_State *L)
|
||||
{
|
||||
const Graphics::Capabilities &caps = instance()->getCapabilities();
|
||||
@@ -3156,6 +3387,9 @@ static const luaL_Reg functions[] =
|
||||
{ "newSpriteBatch", w_newSpriteBatch },
|
||||
{ "newParticleSystem", w_newParticleSystem },
|
||||
{ "newShader", w_newShader },
|
||||
{ "newBuffer", w_newBuffer },
|
||||
{ "newVertexBuffer", w_newVertexBuffer },
|
||||
{ "newIndexBuffer", w_newIndexBuffer },
|
||||
{ "newMesh", w_newMesh },
|
||||
{ "newText", w_newText },
|
||||
{ "_newVideo", w_newVideo },
|
||||
@@ -3203,7 +3437,6 @@ static const luaL_Reg functions[] =
|
||||
|
||||
{ "setShader", w_setShader },
|
||||
{ "getShader", w_getShader },
|
||||
{ "_setDefaultShaderCode", w_setDefaultShaderCode },
|
||||
|
||||
{ "getSupported", w_getSupported },
|
||||
{ "getTextureFormats", w_getTextureFormats },
|
||||
@@ -3286,6 +3519,7 @@ static const lua_CFunction types[] =
|
||||
luaopen_texture,
|
||||
luaopen_font,
|
||||
luaopen_quad,
|
||||
luaopen_graphicsbuffer,
|
||||
luaopen_spritebatch,
|
||||
luaopen_particlesystem,
|
||||
luaopen_shader,
|
||||
@@ -3328,11 +3562,6 @@ extern "C" int luaopen_love_graphics(lua_State *L)
|
||||
else
|
||||
lua_error(L);
|
||||
|
||||
if (luaL_loadbuffer(L, (const char *)graphics_shader_lua, sizeof(graphics_shader_lua), "wrap_GraphicsShader.lua") == 0)
|
||||
lua_call(L, 0, 0);
|
||||
else
|
||||
lua_error(L);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "wrap_Mesh.h"
|
||||
#include "wrap_Text.h"
|
||||
#include "wrap_Video.h"
|
||||
#include "wrap_Buffer.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
namespace love
|
||||
|
||||
@@ -1,520 +0,0 @@
|
||||
R"luastring"--(
|
||||
-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
|
||||
-- There is a matching delimiter at the bottom of the file.
|
||||
|
||||
--[[
|
||||
Copyright (c) 2006-2020 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.
|
||||
--]]
|
||||
|
||||
local table_concat, table_insert = table.concat, table.insert
|
||||
local ipairs = ipairs
|
||||
|
||||
local GLSL = {}
|
||||
|
||||
GLSL.VERSION = { -- index using [target][gles]
|
||||
glsl1 = {[false]="#version 120", [true]="#version 100"},
|
||||
glsl3 = {[false]="#version 330 core", [true]="#version 300 es"},
|
||||
glsl4 = {[false]="#version 430 core", [true]="#version 310 es"},
|
||||
}
|
||||
|
||||
GLSL.SYNTAX = [[
|
||||
#if !defined(GL_ES) && __VERSION__ < 140
|
||||
#define lowp
|
||||
#define mediump
|
||||
#define highp
|
||||
#endif
|
||||
#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH)
|
||||
#define LOVE_HIGHP_OR_MEDIUMP highp
|
||||
#else
|
||||
#define LOVE_HIGHP_OR_MEDIUMP mediump
|
||||
#endif
|
||||
#if __VERSION__ >= 300
|
||||
#define LOVE_IO_LOCATION(x) layout (location = x)
|
||||
#else
|
||||
#define LOVE_IO_LOCATION(x)
|
||||
#endif
|
||||
#define number float
|
||||
#define Image sampler2D
|
||||
#define ArrayImage sampler2DArray
|
||||
#define CubeImage samplerCube
|
||||
#define VolumeImage sampler3D
|
||||
#if __VERSION__ >= 300 && !defined(LOVE_GLSL1_ON_GLSL3)
|
||||
#define DepthImage sampler2DShadow
|
||||
#define DepthArrayImage sampler2DArrayShadow
|
||||
#define DepthCubeImage samplerCubeShadow
|
||||
#endif
|
||||
#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
|
||||
#ifdef GL_OES_standard_derivatives
|
||||
#extension GL_OES_standard_derivatives : enable
|
||||
#endif
|
||||
]]
|
||||
|
||||
-- Uniforms shared by the vertex and pixel shader stages.
|
||||
GLSL.UNIFORMS = [[
|
||||
// According to the GLSL ES 1.0 spec, uniform precision must match between stages,
|
||||
// but we can't guarantee that highp is always supported in fragment shaders...
|
||||
// We *really* don't want to use mediump for these in vertex shaders though.
|
||||
#ifdef LOVE_USE_UNIFORM_BUFFERS
|
||||
layout (std140) uniform love_UniformsPerDrawBuffer {
|
||||
highp vec4 love_UniformsPerDraw[13];
|
||||
};
|
||||
#else
|
||||
uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13];
|
||||
#endif
|
||||
|
||||
// These are initialized in love_initializeBuiltinUniforms below. GLSL ES can't
|
||||
// do it as an initializer.
|
||||
LOVE_HIGHP_OR_MEDIUMP mat4 TransformMatrix;
|
||||
LOVE_HIGHP_OR_MEDIUMP mat4 ProjectionMatrix;
|
||||
LOVE_HIGHP_OR_MEDIUMP mat3 NormalMatrix;
|
||||
|
||||
LOVE_HIGHP_OR_MEDIUMP vec4 love_ScreenSize;
|
||||
LOVE_HIGHP_OR_MEDIUMP vec4 ConstantColor;
|
||||
|
||||
#define TransformProjectionMatrix (ProjectionMatrix * TransformMatrix)
|
||||
|
||||
// Alternate names
|
||||
#define ViewSpaceFromLocal TransformMatrix
|
||||
#define ClipSpaceFromView ProjectionMatrix
|
||||
#define ClipSpaceFromLocal TransformProjectionMatrix
|
||||
#define ViewNormalFromLocal NormalMatrix
|
||||
|
||||
void love_initializeBuiltinUniforms() {
|
||||
TransformMatrix = mat4(
|
||||
love_UniformsPerDraw[0],
|
||||
love_UniformsPerDraw[1],
|
||||
love_UniformsPerDraw[2],
|
||||
love_UniformsPerDraw[3]
|
||||
);
|
||||
|
||||
ProjectionMatrix = mat4(
|
||||
love_UniformsPerDraw[4],
|
||||
love_UniformsPerDraw[5],
|
||||
love_UniformsPerDraw[6],
|
||||
love_UniformsPerDraw[7]
|
||||
);
|
||||
|
||||
NormalMatrix = mat3(
|
||||
love_UniformsPerDraw[8].xyz,
|
||||
love_UniformsPerDraw[9].xyz,
|
||||
love_UniformsPerDraw[10].xyz
|
||||
);
|
||||
|
||||
love_ScreenSize = love_UniformsPerDraw[11];
|
||||
ConstantColor = love_UniformsPerDraw[12];
|
||||
}
|
||||
]]
|
||||
|
||||
GLSL.FUNCTIONS = [[
|
||||
#ifdef GL_ES
|
||||
#if __VERSION__ >= 300 || defined(GL_EXT_texture_array)
|
||||
precision lowp sampler2DArray;
|
||||
#endif
|
||||
#if __VERSION__ >= 300 || defined(GL_OES_texture_3D)
|
||||
precision lowp sampler3D;
|
||||
#endif
|
||||
#if __VERSION__ >= 300
|
||||
precision lowp sampler2DShadow;
|
||||
precision lowp samplerCubeShadow;
|
||||
precision lowp sampler2DArrayShadow;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if __VERSION__ >= 130 && !defined(LOVE_GLSL1_ON_GLSL3)
|
||||
#define Texel texture
|
||||
#else
|
||||
#if __VERSION__ >= 130
|
||||
#define texture2D Texel
|
||||
#define texture3D Texel
|
||||
#define textureCube Texel
|
||||
#define texture2DArray Texel
|
||||
#define love_texture2D texture
|
||||
#define love_texture3D texture
|
||||
#define love_textureCube texture
|
||||
#define love_texture2DArray texture
|
||||
#else
|
||||
#define love_texture2D texture2D
|
||||
#define love_texture3D texture3D
|
||||
#define love_textureCube textureCube
|
||||
#define love_texture2DArray texture2DArray
|
||||
#endif
|
||||
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
|
||||
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
|
||||
#define texture love_texture
|
||||
#endif
|
||||
|
||||
float gammaToLinearPrecise(float c) {
|
||||
return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4);
|
||||
}
|
||||
vec3 gammaToLinearPrecise(vec3 c) {
|
||||
bvec3 leq = lessThanEqual(c, vec3(0.04045));
|
||||
c.r = leq.r ? c.r / 12.92 : pow((c.r + 0.055) / 1.055, 2.4);
|
||||
c.g = leq.g ? c.g / 12.92 : pow((c.g + 0.055) / 1.055, 2.4);
|
||||
c.b = leq.b ? c.b / 12.92 : pow((c.b + 0.055) / 1.055, 2.4);
|
||||
return c;
|
||||
}
|
||||
vec4 gammaToLinearPrecise(vec4 c) { return vec4(gammaToLinearPrecise(c.rgb), c.a); }
|
||||
float linearToGammaPrecise(float c) {
|
||||
return c < 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1.0 / 2.4) - 0.055;
|
||||
}
|
||||
vec3 linearToGammaPrecise(vec3 c) {
|
||||
bvec3 lt = lessThanEqual(c, vec3(0.0031308));
|
||||
c.r = lt.r ? c.r * 12.92 : 1.055 * pow(c.r, 1.0 / 2.4) - 0.055;
|
||||
c.g = lt.g ? c.g * 12.92 : 1.055 * pow(c.g, 1.0 / 2.4) - 0.055;
|
||||
c.b = lt.b ? c.b * 12.92 : 1.055 * pow(c.b, 1.0 / 2.4) - 0.055;
|
||||
return c;
|
||||
}
|
||||
vec4 linearToGammaPrecise(vec4 c) { return vec4(linearToGammaPrecise(c.rgb), c.a); }
|
||||
|
||||
// http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1
|
||||
|
||||
mediump float gammaToLinearFast(mediump float c) { return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); }
|
||||
mediump vec3 gammaToLinearFast(mediump vec3 c) { return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); }
|
||||
mediump vec4 gammaToLinearFast(mediump vec4 c) { return vec4(gammaToLinearFast(c.rgb), c.a); }
|
||||
|
||||
mediump float linearToGammaFast(mediump float c) { return max(1.055 * pow(max(c, 0.0), 0.41666666) - 0.055, 0.0); }
|
||||
mediump vec3 linearToGammaFast(mediump vec3 c) { return max(1.055 * pow(max(c, vec3(0.0)), vec3(0.41666666)) - 0.055, vec3(0.0)); }
|
||||
mediump vec4 linearToGammaFast(mediump vec4 c) { return vec4(linearToGammaFast(c.rgb), c.a); }
|
||||
|
||||
#define gammaToLinear gammaToLinearFast
|
||||
#define linearToGamma linearToGammaFast
|
||||
|
||||
#ifdef LOVE_GAMMA_CORRECT
|
||||
#define gammaCorrectColor gammaToLinear
|
||||
#define unGammaCorrectColor linearToGamma
|
||||
#define gammaCorrectColorPrecise gammaToLinearPrecise
|
||||
#define unGammaCorrectColorPrecise linearToGammaPrecise
|
||||
#define gammaCorrectColorFast gammaToLinearFast
|
||||
#define unGammaCorrectColorFast linearToGammaFast
|
||||
#else
|
||||
#define gammaCorrectColor
|
||||
#define unGammaCorrectColor
|
||||
#define gammaCorrectColorPrecise
|
||||
#define unGammaCorrectColorPrecise
|
||||
#define gammaCorrectColorFast
|
||||
#define unGammaCorrectColorFast
|
||||
#endif]]
|
||||
|
||||
GLSL.VERTEX = {
|
||||
HEADER = [[
|
||||
#define love_Position gl_Position
|
||||
|
||||
#if __VERSION__ >= 130
|
||||
#define attribute in
|
||||
#define varying out
|
||||
#ifndef LOVE_GLSL1_ON_GLSL3
|
||||
#define love_VertexID gl_VertexID
|
||||
#define love_InstanceID gl_InstanceID
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef GL_ES
|
||||
uniform mediump float love_PointSize;
|
||||
#endif]],
|
||||
|
||||
FUNCTIONS = [[
|
||||
void setPointSize() {
|
||||
#ifdef GL_ES
|
||||
gl_PointSize = love_PointSize;
|
||||
#endif
|
||||
}]],
|
||||
|
||||
MAIN = [[
|
||||
LOVE_IO_LOCATION(0) attribute vec4 VertexPosition;
|
||||
LOVE_IO_LOCATION(1) attribute vec4 VertexTexCoord;
|
||||
LOVE_IO_LOCATION(2) attribute vec4 VertexColor;
|
||||
|
||||
varying vec4 VaryingTexCoord;
|
||||
varying vec4 VaryingColor;
|
||||
|
||||
vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition);
|
||||
|
||||
void main() {
|
||||
love_initializeBuiltinUniforms();
|
||||
VaryingTexCoord = VertexTexCoord;
|
||||
VaryingColor = gammaCorrectColor(VertexColor) * ConstantColor;
|
||||
setPointSize();
|
||||
love_Position = position(ClipSpaceFromLocal, VertexPosition);
|
||||
}]],
|
||||
}
|
||||
|
||||
GLSL.PIXEL = {
|
||||
HEADER = [[
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
#define love_MaxRenderTargets gl_MaxDrawBuffers
|
||||
|
||||
#if __VERSION__ >= 130
|
||||
#define varying in
|
||||
// Some drivers seem to make the pixel shader do more work when multiple
|
||||
// pixel shader outputs are defined, even when only one is actually used.
|
||||
// TODO: We should use reflection or something instead of this, to determine
|
||||
// how many outputs are actually used in the shader code.
|
||||
#ifdef LOVE_MULTI_RENDER_TARGETS
|
||||
LOVE_IO_LOCATION(0) out vec4 love_RenderTargets[love_MaxRenderTargets];
|
||||
#define love_PixelColor love_RenderTargets[0]
|
||||
#else
|
||||
LOVE_IO_LOCATION(0) out vec4 love_PixelColor;
|
||||
#endif
|
||||
#else
|
||||
#ifdef LOVE_MULTI_RENDER_TARGETS
|
||||
#define love_RenderTargets gl_FragData
|
||||
#endif
|
||||
#define love_PixelColor gl_FragColor
|
||||
#endif
|
||||
|
||||
// Legacy
|
||||
#define love_MaxCanvases love_MaxRenderTargets
|
||||
#define love_Canvases love_RenderTargets
|
||||
#ifdef LOVE_MULTI_RENDER_TARGETS
|
||||
#define LOVE_MULTI_CANVASES 1
|
||||
#endif
|
||||
|
||||
// See Shader::updateScreenParams in Shader.cpp.
|
||||
#define love_PixelCoord (vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w))]],
|
||||
|
||||
FUNCTIONS = [[
|
||||
uniform sampler2D love_VideoYChannel;
|
||||
uniform sampler2D love_VideoCbChannel;
|
||||
uniform sampler2D love_VideoCrChannel;
|
||||
|
||||
vec4 VideoTexel(vec2 texcoords) {
|
||||
vec3 yuv;
|
||||
yuv[0] = Texel(love_VideoYChannel, texcoords).r;
|
||||
yuv[1] = Texel(love_VideoCbChannel, texcoords).r;
|
||||
yuv[2] = Texel(love_VideoCrChannel, texcoords).r;
|
||||
yuv += vec3(-0.0627451017, -0.501960814, -0.501960814);
|
||||
|
||||
vec4 color;
|
||||
color.r = dot(yuv, vec3(1.164, 0.000, 1.596));
|
||||
color.g = dot(yuv, vec3(1.164, -0.391, -0.813));
|
||||
color.b = dot(yuv, vec3(1.164, 2.018, 0.000));
|
||||
color.a = 1.0;
|
||||
|
||||
return gammaCorrectColor(color);
|
||||
}]],
|
||||
|
||||
MAIN = [[
|
||||
uniform sampler2D MainTex;
|
||||
varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord;
|
||||
varying mediump vec4 VaryingColor;
|
||||
|
||||
vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord);
|
||||
|
||||
void main() {
|
||||
love_initializeBuiltinUniforms();
|
||||
love_PixelColor = effect(VaryingColor, MainTex, VaryingTexCoord.st, love_PixelCoord);
|
||||
}]],
|
||||
|
||||
MAIN_CUSTOM = [[
|
||||
varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord;
|
||||
varying mediump vec4 VaryingColor;
|
||||
|
||||
void effect();
|
||||
|
||||
void main() {
|
||||
love_initializeBuiltinUniforms();
|
||||
effect();
|
||||
}]],
|
||||
}
|
||||
|
||||
local function getLanguageTarget(code)
|
||||
if not code then return nil end
|
||||
return (code:match("^%s*#pragma language (%w+)")) or "glsl1"
|
||||
end
|
||||
|
||||
local function createShaderStageCode(stage, code, lang, gles, glsl1on3, gammacorrect, custom, multirendertarget, useubo)
|
||||
stage = stage:upper()
|
||||
local lines = {
|
||||
GLSL.VERSION[lang][gles],
|
||||
"#define " ..stage .. " " .. stage,
|
||||
glsl1on3 and "#define LOVE_GLSL1_ON_GLSL3 1" or "",
|
||||
gammacorrect and "#define LOVE_GAMMA_CORRECT 1" or "",
|
||||
multirendertarget and "#define LOVE_MULTI_RENDER_TARGETS 1" or "",
|
||||
useubo and "#define LOVE_USE_UNIFORM_BUFFERS 1" or "",
|
||||
GLSL.SYNTAX,
|
||||
GLSL[stage].HEADER,
|
||||
GLSL.UNIFORMS,
|
||||
GLSL.FUNCTIONS,
|
||||
GLSL[stage].FUNCTIONS,
|
||||
custom and GLSL[stage].MAIN_CUSTOM or GLSL[stage].MAIN,
|
||||
((lang == "glsl1" or glsl1on3) and not gles) and "#line 0" or "#line 1",
|
||||
code,
|
||||
}
|
||||
return table_concat(lines, "\n")
|
||||
end
|
||||
|
||||
local function isVertexCode(code)
|
||||
return code:match("vec4%s+position%s*%(") ~= nil
|
||||
end
|
||||
|
||||
local function isPixelCode(code)
|
||||
if code:match("vec4%s+effect%s*%(") then
|
||||
return true
|
||||
elseif code:match("void%s+effect%s*%(") then -- custom effect function
|
||||
local mrt = (code:match("love_RenderTargets") ~= nil) or (code:match("love_Canvases") ~= nil)
|
||||
return true, true, mrt
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function love.graphics._shaderCodeToGLSL(gles, arg1, arg2)
|
||||
local vertexcode, pixelcode
|
||||
local is_custompixel = false -- whether pixel code has "effects" function instead of "effect"
|
||||
local is_multicanvas = false
|
||||
|
||||
if arg1 then
|
||||
if isVertexCode(arg1) then
|
||||
vertexcode = arg1 -- first arg contains vertex shader code
|
||||
end
|
||||
|
||||
local ispixel, isCustomPixel, isMultiCanvas = isPixelCode(arg1)
|
||||
if ispixel then
|
||||
pixelcode = arg1 -- first arg contains pixel shader code
|
||||
is_custompixel, is_multicanvas = isCustomPixel, isMultiCanvas
|
||||
end
|
||||
end
|
||||
|
||||
if arg2 then
|
||||
if isVertexCode(arg2) then
|
||||
vertexcode = arg2 -- second arg contains vertex shader code
|
||||
end
|
||||
|
||||
local ispixel, isCustomPixel, isMultiCanvas = isPixelCode(arg2)
|
||||
if ispixel then
|
||||
pixelcode = arg2 -- second arg contains pixel shader code
|
||||
is_custompixel, is_multicanvas = isCustomPixel, isMultiCanvas
|
||||
end
|
||||
end
|
||||
|
||||
local graphicsfeatures = love.graphics.getSupported()
|
||||
local supportsGLSL3 = graphicsfeatures.glsl3
|
||||
local supportsGLSL4 = graphicsfeatures.glsl4
|
||||
local gammacorrect = love.graphics.isGammaCorrect()
|
||||
local renderer = love.graphics.getRenderer()
|
||||
local useubo = renderer == "Metal"
|
||||
|
||||
local targetlang = getLanguageTarget(pixelcode or vertexcode)
|
||||
if getLanguageTarget(vertexcode or pixelcode) ~= targetlang then
|
||||
error("vertex and pixel shader languages must match", 2)
|
||||
end
|
||||
|
||||
if targetlang == "glsl3" and not supportsGLSL3 then
|
||||
error("GLSL 3 shaders are not supported on this system.", 2)
|
||||
end
|
||||
|
||||
if targetlang == "glsl4" and not supportsGLSL4 then
|
||||
error("GLSL 4 shaders are not supported on this system.", 2)
|
||||
end
|
||||
|
||||
if targetlang ~= nil and not GLSL.VERSION[targetlang] then
|
||||
error("Invalid shader language: " .. targetlang, 2)
|
||||
end
|
||||
|
||||
local lang = targetlang or "glsl1"
|
||||
local glsl1on3 = false
|
||||
if lang == "glsl1" and supportsGLSL3 then
|
||||
lang = "glsl3"
|
||||
glsl1on3 = true
|
||||
end
|
||||
|
||||
if vertexcode then
|
||||
vertexcode = createShaderStageCode("VERTEX", vertexcode, lang, gles, glsl1on3, gammacorrect, false, false, useubo)
|
||||
end
|
||||
if pixelcode then
|
||||
pixelcode = createShaderStageCode("PIXEL", pixelcode, lang, gles, glsl1on3, gammacorrect, is_custompixel, is_multicanvas, useubo)
|
||||
end
|
||||
|
||||
return vertexcode, pixelcode
|
||||
end
|
||||
|
||||
local defaultcode = {
|
||||
vertex = [[
|
||||
vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition) {
|
||||
return clipSpaceFromLocal * localPosition;
|
||||
}]],
|
||||
pixel = [[
|
||||
vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) {
|
||||
return Texel(tex, texcoord) * vcolor;
|
||||
}]],
|
||||
videopixel = [[
|
||||
void effect() {
|
||||
love_PixelColor = VideoTexel(VaryingTexCoord.xy) * VaryingColor;
|
||||
}]],
|
||||
arraypixel = [[
|
||||
uniform ArrayImage MainTex;
|
||||
void effect() {
|
||||
love_PixelColor = Texel(MainTex, VaryingTexCoord.xyz) * VaryingColor;
|
||||
}]],
|
||||
}
|
||||
|
||||
local defaults = {}
|
||||
local defaults_gammacorrect = {}
|
||||
|
||||
local langs = {
|
||||
glsl1 = {target="glsl1", gles=false},
|
||||
essl1 = {target="glsl1", gles=true},
|
||||
glsl3 = {target="glsl3", gles=false},
|
||||
essl3 = {target="glsl3", gles=true},
|
||||
}
|
||||
|
||||
-- FIXME: this is temporary until a glslang pull request is merged in.
|
||||
local useubo = true
|
||||
|
||||
for lang, info in pairs(langs) do
|
||||
for _, gammacorrect in ipairs{false, true} do
|
||||
local t = gammacorrect and defaults_gammacorrect or defaults
|
||||
t[lang] = {
|
||||
vertex = createShaderStageCode("VERTEX", defaultcode.vertex, info.target, info.gles, false, gammacorrect, false, false, useubo),
|
||||
pixel = createShaderStageCode("PIXEL", defaultcode.pixel, info.target, info.gles, false, gammacorrect, false, false, useubo),
|
||||
videopixel = createShaderStageCode("PIXEL", defaultcode.videopixel, info.target, info.gles, false, gammacorrect, true, false, useubo),
|
||||
arraypixel = createShaderStageCode("PIXEL", defaultcode.arraypixel, info.target, info.gles, false, gammacorrect, true, false, useubo),
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
love.graphics._setDefaultShaderCode(defaults, defaults_gammacorrect)
|
||||
|
||||
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
|
||||
--)luastring"--"
|
||||
+133
-184
@@ -20,6 +20,7 @@
|
||||
|
||||
// LOVE
|
||||
#include "wrap_Mesh.h"
|
||||
#include "wrap_Buffer.h"
|
||||
#include "Texture.h"
|
||||
#include "wrap_Texture.h"
|
||||
|
||||
@@ -36,140 +37,6 @@ Mesh *luax_checkmesh(lua_State *L, int idx)
|
||||
return luax_checktype<Mesh>(L, idx);
|
||||
}
|
||||
|
||||
static const double defaultComponents[] = {0.0, 0.0, 0.0, 1.0};
|
||||
|
||||
template <typename T>
|
||||
static inline size_t writeData(lua_State *L, int startidx, int components, char *data)
|
||||
{
|
||||
auto componentdata = (T *) data;
|
||||
|
||||
for (int i = 0; i < components; i++)
|
||||
componentdata[i] = (T) (luaL_optnumber(L, startidx + i, defaultComponents[i]));
|
||||
|
||||
return sizeof(T) * components;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline size_t writeSNormData(lua_State *L, int startidx, int components, char *data)
|
||||
{
|
||||
auto componentdata = (T *) data;
|
||||
auto maxval = std::numeric_limits<T>::max();
|
||||
|
||||
for (int i = 0; i < components; i++)
|
||||
componentdata[i] = (T) (luax_optnumberclamped(L, startidx + i, -1.0, 1.0, defaultComponents[i]) * maxval);
|
||||
|
||||
return sizeof(T) * components;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline size_t writeUNormData(lua_State *L, int startidx, int components, char *data)
|
||||
{
|
||||
auto componentdata = (T *) data;
|
||||
auto maxval = std::numeric_limits<T>::max();
|
||||
|
||||
for (int i = 0; i < components; i++)
|
||||
componentdata[i] = (T) (luax_optnumberclamped01(L, startidx + i, 1.0) * maxval);
|
||||
|
||||
return sizeof(T) * components;
|
||||
}
|
||||
|
||||
char *luax_writeAttributeData(lua_State *L, int startidx, vertex::DataType type, int components, char *data)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case vertex::DATA_SNORM8:
|
||||
return data + writeSNormData<int8>(L, startidx, components, data);
|
||||
case vertex::DATA_UNORM8:
|
||||
return data + writeUNormData<uint8>(L, startidx, components, data);
|
||||
case vertex::DATA_INT8:
|
||||
return data + writeData<int8>(L, startidx, components, data);
|
||||
case vertex::DATA_UINT8:
|
||||
return data + writeData<uint8>(L, startidx, components, data);
|
||||
case vertex::DATA_SNORM16:
|
||||
return data + writeSNormData<int16>(L, startidx, components, data);
|
||||
case vertex::DATA_UNORM16:
|
||||
return data + writeUNormData<uint16>(L, startidx, components, data);
|
||||
case vertex::DATA_INT16:
|
||||
return data + writeData<int16>(L, startidx, components, data);
|
||||
case vertex::DATA_UINT16:
|
||||
return data + writeData<uint16>(L, startidx, components, data);
|
||||
case vertex::DATA_INT32:
|
||||
return data + writeData<int32>(L, startidx, components, data);
|
||||
case vertex::DATA_UINT32:
|
||||
return data + writeData<uint32>(L, startidx, components, data);
|
||||
case vertex::DATA_FLOAT:
|
||||
return data + writeData<float>(L, startidx, components, data);
|
||||
default:
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline size_t readData(lua_State *L, int components, const char *data)
|
||||
{
|
||||
auto componentdata = (const T *) data;
|
||||
|
||||
for (int i = 0; i < components; i++)
|
||||
lua_pushnumber(L, (lua_Number) componentdata[i]);
|
||||
|
||||
return sizeof(T) * components;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline size_t readSNormData(lua_State *L, int components, const char *data)
|
||||
{
|
||||
auto componentdata = (const T *) data;
|
||||
auto maxval = std::numeric_limits<T>::max();
|
||||
|
||||
for (int i = 0; i < components; i++)
|
||||
lua_pushnumber(L, std::max(-1.0, (lua_Number) componentdata[i] / (lua_Number)maxval));
|
||||
|
||||
return sizeof(T) * components;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline size_t readUNormData(lua_State *L, int components, const char *data)
|
||||
{
|
||||
auto componentdata = (const T *) data;
|
||||
auto maxval = std::numeric_limits<T>::max();
|
||||
|
||||
for (int i = 0; i < components; i++)
|
||||
lua_pushnumber(L, (lua_Number) componentdata[i] / (lua_Number)maxval);
|
||||
|
||||
return sizeof(T) * components;
|
||||
}
|
||||
|
||||
const char *luax_readAttributeData(lua_State *L, vertex::DataType type, int components, const char *data)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case vertex::DATA_SNORM8:
|
||||
return data + readSNormData<int8>(L, components, data);
|
||||
case vertex::DATA_UNORM8:
|
||||
return data + readUNormData<uint8>(L, components, data);
|
||||
case vertex::DATA_INT8:
|
||||
return data + readData<int8>(L, components, data);
|
||||
case vertex::DATA_UINT8:
|
||||
return data + readData<uint8>(L, components, data);
|
||||
case vertex::DATA_SNORM16:
|
||||
return data + readSNormData<int16>(L, components, data);
|
||||
case vertex::DATA_UNORM16:
|
||||
return data + readUNormData<uint16>(L, components, data);
|
||||
case vertex::DATA_INT16:
|
||||
return data + readData<int16>(L, components, data);
|
||||
case vertex::DATA_UINT16:
|
||||
return data + readData<uint16>(L, components, data);
|
||||
case vertex::DATA_INT32:
|
||||
return data + readData<int32>(L, components, data);
|
||||
case vertex::DATA_UINT32:
|
||||
return data + readData<uint32>(L, components, data);
|
||||
case vertex::DATA_FLOAT:
|
||||
return data + readData<float>(L, components, data);
|
||||
default:
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
int w_Mesh_setVertices(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
@@ -188,7 +55,7 @@ int w_Mesh_setVertices(lua_State *L)
|
||||
size_t byteoffset = vertstart * stride;
|
||||
int totalverts = (int) t->getVertexCount();
|
||||
|
||||
if (vertstart >= totalverts)
|
||||
if (vertstart >= totalverts || vertstart < 0)
|
||||
return luaL_error(L, "Invalid vertex start index (must be between 1 and %d)", totalverts);
|
||||
|
||||
if (luax_istype(L, 2, Data::type))
|
||||
@@ -215,11 +82,11 @@ int w_Mesh_setVertices(lua_State *L)
|
||||
if (vertstart + vertcount > totalverts)
|
||||
return luaL_error(L, "Too many vertices (expected at most %d, got %d)", totalverts - vertstart, vertcount);
|
||||
|
||||
const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
|
||||
const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
|
||||
|
||||
int ncomponents = 0;
|
||||
for (const Mesh::AttribFormat &format : vertexformat)
|
||||
ncomponents += format.components;
|
||||
for (const Buffer::DataMember &member : vertexformat)
|
||||
ncomponents += member.info.components;
|
||||
|
||||
char *data = (char *) t->mapVertexData() + byteoffset;
|
||||
|
||||
@@ -235,14 +102,16 @@ int w_Mesh_setVertices(lua_State *L)
|
||||
|
||||
int idx = -ncomponents;
|
||||
|
||||
for (const Mesh::AttribFormat &format : vertexformat)
|
||||
for (const Buffer::DataMember &member : vertexformat)
|
||||
{
|
||||
// Fetch the values from Lua and store them in data buffer.
|
||||
data = luax_writeAttributeData(L, idx, format.type, format.components, data);
|
||||
idx += format.components;
|
||||
luax_writebufferdata(L, idx, member.decl.format, data + member.offset);
|
||||
idx += member.info.components;
|
||||
}
|
||||
|
||||
lua_pop(L, ncomponents + 1);
|
||||
|
||||
data += stride;
|
||||
}
|
||||
|
||||
t->unmapVertexData(byteoffset, vertcount * stride);
|
||||
@@ -256,34 +125,34 @@ int w_Mesh_setVertex(lua_State *L)
|
||||
|
||||
bool istable = lua_istable(L, 3);
|
||||
|
||||
const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
|
||||
|
||||
const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
|
||||
char *data = (char *) t->getVertexScratchBuffer();
|
||||
char *writtendata = data;
|
||||
|
||||
int idx = istable ? 1 : 3;
|
||||
|
||||
if (istable)
|
||||
{
|
||||
for (const Mesh::AttribFormat &format : vertexformat)
|
||||
for (const Buffer::DataMember &member : vertexformat)
|
||||
{
|
||||
for (int i = idx; i < idx + format.components; i++)
|
||||
int components = member.info.components;
|
||||
|
||||
for (int i = idx; i < idx + components; i++)
|
||||
lua_rawgeti(L, 3, i);
|
||||
|
||||
// Fetch the values from Lua and store them in data buffer.
|
||||
writtendata = luax_writeAttributeData(L, -format.components, format.type, format.components, writtendata);
|
||||
luax_writebufferdata(L, -components, member.decl.format, data + member.offset);
|
||||
|
||||
idx += format.components;
|
||||
lua_pop(L, format.components);
|
||||
idx += components;
|
||||
lua_pop(L, components);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const Mesh::AttribFormat &format : vertexformat)
|
||||
for (const Buffer::DataMember &member : vertexformat)
|
||||
{
|
||||
// Fetch the values from Lua and store them in data buffer.
|
||||
writtendata = luax_writeAttributeData(L, idx, format.type, format.components, writtendata);
|
||||
idx += format.components;
|
||||
luax_writebufferdata(L, idx, member.decl.format, data + member.offset);
|
||||
idx += member.info.components;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,19 +165,18 @@ int w_Mesh_getVertex(lua_State *L)
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
size_t index = (size_t) luaL_checkinteger(L, 2) - 1;
|
||||
|
||||
const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
|
||||
const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
|
||||
|
||||
char *data = (char *) t->getVertexScratchBuffer();
|
||||
const char *readdata = data;
|
||||
|
||||
luax_catchexcept(L, [&](){ t->getVertex(index, data, t->getVertexStride()); });
|
||||
|
||||
int n = 0;
|
||||
|
||||
for (const Mesh::AttribFormat &format : vertexformat)
|
||||
for (const Buffer::DataMember &member : vertexformat)
|
||||
{
|
||||
readdata = luax_readAttributeData(L, format.type, format.components, readdata);
|
||||
n += format.components;
|
||||
luax_readbufferdata(L, member.decl.format, data + member.offset);
|
||||
n += member.info.components;
|
||||
}
|
||||
|
||||
return n;
|
||||
@@ -320,15 +188,18 @@ int w_Mesh_setVertexAttribute(lua_State *L)
|
||||
size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1;
|
||||
int attribindex = (int) luaL_checkinteger(L, 3) - 1;
|
||||
|
||||
vertex::DataType type;
|
||||
int components;
|
||||
luax_catchexcept(L, [&](){ type = t->getAttributeInfo(attribindex, components); });
|
||||
const auto &vertexformat = t->getVertexFormat();
|
||||
|
||||
if (attribindex < 0 || attribindex >= (int) vertexformat.size())
|
||||
return luaL_error(L, "Invalid vertex attribute index: %d", attribindex + 1);
|
||||
|
||||
const Buffer::DataMember &member = vertexformat[attribindex];
|
||||
|
||||
// Maximum possible size for a single vertex attribute.
|
||||
char data[sizeof(float) * 4];
|
||||
|
||||
// Fetch the values from Lua and store them in the data buffer.
|
||||
luax_writeAttributeData(L, 4, type, components, data);
|
||||
luax_writebufferdata(L, 4, member.decl.format, data);
|
||||
|
||||
luax_catchexcept(L, [&](){ t->setVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); });
|
||||
return 0;
|
||||
@@ -340,17 +211,20 @@ int w_Mesh_getVertexAttribute(lua_State *L)
|
||||
size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1;
|
||||
int attribindex = (int) luaL_checkinteger(L, 3) - 1;
|
||||
|
||||
vertex::DataType type;
|
||||
int components;
|
||||
luax_catchexcept(L, [&](){ type = t->getAttributeInfo(attribindex, components); });
|
||||
const auto &vertexformat = t->getVertexFormat();
|
||||
|
||||
if (attribindex < 0 || attribindex >= (int) vertexformat.size())
|
||||
return luaL_error(L, "Invalid vertex attribute index: %d", attribindex + 1);
|
||||
|
||||
const Buffer::DataMember &member = vertexformat[attribindex];
|
||||
|
||||
// Maximum possible size for a single vertex attribute.
|
||||
char data[sizeof(float) * 4];
|
||||
|
||||
luax_catchexcept(L, [&](){ t->getVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); });
|
||||
|
||||
luax_readAttributeData(L, type, components, data);
|
||||
return components;
|
||||
luax_readbufferdata(L, member.decl.format, data);
|
||||
return member.info.components;
|
||||
}
|
||||
|
||||
int w_Mesh_getVertexCount(lua_State *L)
|
||||
@@ -364,28 +238,27 @@ int w_Mesh_getVertexFormat(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
|
||||
const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
|
||||
const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
|
||||
lua_createtable(L, (int) vertexformat.size(), 0);
|
||||
|
||||
const char *tname = nullptr;
|
||||
|
||||
for (size_t i = 0; i < vertexformat.size(); i++)
|
||||
{
|
||||
if (!vertex::getConstant(vertexformat[i].type, tname))
|
||||
return luax_enumerror(L, "vertex attribute data type", vertex::getConstants(vertexformat[i].type), tname);
|
||||
const auto &decl = vertexformat[i].decl;
|
||||
|
||||
if (!getConstant(decl.format, tname))
|
||||
return luax_enumerror(L, "vertex attribute data type", getConstants(decl.format), tname);
|
||||
|
||||
lua_createtable(L, 3, 0);
|
||||
|
||||
lua_pushstring(L, vertexformat[i].name.c_str());
|
||||
lua_pushstring(L, decl.name.c_str());
|
||||
lua_rawseti(L, -2, 1);
|
||||
|
||||
lua_pushstring(L, tname);
|
||||
lua_rawseti(L, -2, 2);
|
||||
|
||||
lua_pushinteger(L, vertexformat[i].components);
|
||||
lua_rawseti(L, -2, 3);
|
||||
|
||||
// format[i] = {name, type, components}
|
||||
// format[i] = {name, type}
|
||||
lua_rawseti(L, -2, (int) i + 1);
|
||||
}
|
||||
|
||||
@@ -415,16 +288,29 @@ int w_Mesh_attachAttribute(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
const char *name = luaL_checkstring(L, 2);
|
||||
Mesh *mesh = luax_checkmesh(L, 3);
|
||||
|
||||
Buffer *buffer = nullptr;
|
||||
if (luax_istype(L, 3, Buffer::type))
|
||||
{
|
||||
buffer = luax_checktype<Buffer>(L, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mesh *mesh = luax_checkmesh(L, 3);
|
||||
buffer = mesh->getVertexBuffer();
|
||||
if (buffer == nullptr)
|
||||
return luaL_error(L, "Mesh does not have its own vertex buffer.");
|
||||
luax_markdeprecated(L, "Mesh:attachAttribute(name, mesh, ...)", API_METHOD, DEPRECATED_REPLACED, "Mesh:attachAttribute(name, buffer, ...)");
|
||||
}
|
||||
|
||||
AttributeStep step = STEP_PER_VERTEX;
|
||||
const char *stepstr = lua_isnoneornil(L, 4) ? nullptr : luaL_checkstring(L, 4);
|
||||
if (stepstr != nullptr && !vertex::getConstant(stepstr, step))
|
||||
return luax_enumerror(L, "vertex attribute step", vertex::getConstants(step), stepstr);
|
||||
if (stepstr != nullptr && !getConstant(stepstr, step))
|
||||
return luax_enumerror(L, "vertex attribute step", getConstants(step), stepstr);
|
||||
|
||||
const char *attachname = luaL_optstring(L, 5, name);
|
||||
|
||||
luax_catchexcept(L, [&](){ t->attachAttribute(name, mesh, attachname, step); });
|
||||
luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer, attachname, step); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -438,6 +324,48 @@ int w_Mesh_detachAttribute(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Mesh_getAttachedAttributes(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
const auto &attributes = t->getAttachedAttributes();
|
||||
|
||||
lua_createtable(L, (int) attributes.size(), 0);
|
||||
|
||||
for (int i = 0; i < (int) attributes.size(); i++)
|
||||
{
|
||||
const auto &attrib = attributes[i];
|
||||
|
||||
lua_createtable(L, 4, 0);
|
||||
|
||||
luax_pushstring(L, attrib.name);
|
||||
lua_rawseti(L, -1, 1);
|
||||
|
||||
luax_pushtype(L, attrib.buffer.get());
|
||||
lua_rawseti(L, -1, 2);
|
||||
|
||||
const char *stepstr = nullptr;
|
||||
if (!getConstant(attrib.step, stepstr))
|
||||
return luaL_error(L, "Invalid vertex attribute step.");
|
||||
lua_pushstring(L, stepstr);
|
||||
lua_rawseti(L, -1, 3);
|
||||
|
||||
const Buffer::DataMember &member = attrib.buffer->getDataMember(attrib.indexInBuffer);
|
||||
luax_pushstring(L, member.decl.name);
|
||||
lua_rawseti(L, -1, 4);
|
||||
|
||||
lua_rawseti(L, -1, i + 1);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Mesh_getVertexBuffer(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
luax_pushtype(L, t->getVertexBuffer());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Mesh_flush(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
@@ -462,10 +390,10 @@ int w_Mesh_setVertexMap(lua_State *L)
|
||||
|
||||
const char *indextypestr = luaL_checkstring(L, 3);
|
||||
IndexDataType indextype;
|
||||
if (!vertex::getConstant(indextypestr, indextype))
|
||||
return luax_enumerror(L, "index data type", vertex::getConstants(indextype), indextypestr);
|
||||
if (!getConstant(indextypestr, indextype))
|
||||
return luax_enumerror(L, "index data type", getConstants(indextype), indextypestr);
|
||||
|
||||
size_t datatypesize = vertex::getIndexDataSize(indextype);
|
||||
size_t datatypesize = getIndexDataSize(indextype);
|
||||
|
||||
int indexcount = (int) luaL_optinteger(L, 4, d->getSize() / datatypesize);
|
||||
|
||||
@@ -528,6 +456,23 @@ int w_Mesh_getVertexMap(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Mesh_setIndexBuffer(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
Buffer *b = nullptr;
|
||||
if (!lua_isnoneornil(L, 2))
|
||||
b = luax_checkbuffer(L, 2);
|
||||
luax_catchexcept(L, [&]() { t->setIndexBuffer(b); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Mesh_getIndexBuffer(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
luax_pushtype(L, t->getIndexBuffer());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Mesh_setTexture(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
@@ -561,8 +506,8 @@ int w_Mesh_setDrawMode(lua_State *L)
|
||||
const char *str = luaL_checkstring(L, 2);
|
||||
PrimitiveType mode;
|
||||
|
||||
if (!vertex::getConstant(str, mode))
|
||||
return luax_enumerror(L, "mesh draw mode", vertex::getConstants(mode), str);
|
||||
if (!getConstant(str, mode))
|
||||
return luax_enumerror(L, "mesh draw mode", getConstants(mode), str);
|
||||
|
||||
t->setDrawMode(mode);
|
||||
return 0;
|
||||
@@ -574,7 +519,7 @@ int w_Mesh_getDrawMode(lua_State *L)
|
||||
PrimitiveType mode = t->getDrawMode();
|
||||
const char *str;
|
||||
|
||||
if (!vertex::getConstant(mode, str))
|
||||
if (!getConstant(mode, str))
|
||||
return luaL_error(L, "Unknown mesh draw mode.");
|
||||
|
||||
lua_pushstring(L, str);
|
||||
@@ -624,9 +569,13 @@ static const luaL_Reg w_Mesh_functions[] =
|
||||
{ "isAttributeEnabled", w_Mesh_isAttributeEnabled },
|
||||
{ "attachAttribute", w_Mesh_attachAttribute },
|
||||
{ "detachAttribute", w_Mesh_detachAttribute },
|
||||
{ "getAttachedAttributes", w_Mesh_getAttachedAttributes },
|
||||
{ "getVertexBuffer", w_Mesh_getVertexBuffer },
|
||||
{ "flush", w_Mesh_flush },
|
||||
{ "setVertexMap", w_Mesh_setVertexMap },
|
||||
{ "getVertexMap", w_Mesh_getVertexMap },
|
||||
{ "setIndexBuffer", w_Mesh_setIndexBuffer },
|
||||
{ "getIndexBuffer", w_Mesh_getIndexBuffer },
|
||||
{ "setTexture", w_Mesh_setTexture },
|
||||
{ "getTexture", w_Mesh_getTexture },
|
||||
{ "setDrawMode", w_Mesh_setDrawMode },
|
||||
|
||||
@@ -30,9 +30,6 @@ namespace love
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
char *luax_writeAttributeData(lua_State *L, int startidx, vertex::DataType type, int components, char *data);
|
||||
const char *luax_readAttributeData(lua_State *L, vertex::DataType type, int components, const char *data);
|
||||
|
||||
Mesh *luax_checkmesh(lua_State *L, int idx);
|
||||
extern "C" int luaopen_mesh(lua_State *L);
|
||||
|
||||
|
||||
@@ -287,6 +287,23 @@ int w_Shader_sendTextures(lua_State *L, int startidx, Shader *shader, const Shad
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Shader_sendBuffers(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info)
|
||||
{
|
||||
int count = _getCount(L, startidx, info);
|
||||
|
||||
std::vector<Buffer *> buffers;
|
||||
buffers.reserve(count);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Buffer *buffer = luax_checktype<Buffer>(L, startidx + i);
|
||||
buffers.push_back(buffer);
|
||||
}
|
||||
|
||||
luax_catchexcept(L, [&]() { shader->sendBuffers(info, buffers.data(), count); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int w_Shader_sendLuaValues(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info, const char *name)
|
||||
{
|
||||
switch (info->baseType)
|
||||
@@ -303,6 +320,8 @@ static int w_Shader_sendLuaValues(lua_State *L, int startidx, Shader *shader, co
|
||||
return w_Shader_sendBooleans(L, startidx, shader, info);
|
||||
case Shader::UNIFORM_SAMPLER:
|
||||
return w_Shader_sendTextures(L, startidx, shader, info);
|
||||
case Shader::UNIFORM_TEXELBUFFER:
|
||||
return w_Shader_sendBuffers(L, startidx, shader, info);
|
||||
default:
|
||||
return luaL_error(L, "Unknown variable type for shader uniform '%s", name);
|
||||
}
|
||||
@@ -313,20 +332,36 @@ static int w_Shader_sendData(lua_State *L, int startidx, Shader *shader, const S
|
||||
if (info->baseType == Shader::UNIFORM_SAMPLER)
|
||||
return luaL_error(L, "Uniform sampler values (textures) cannot be sent to Shaders via Data objects.");
|
||||
|
||||
bool columnmajor = false;
|
||||
if (info->baseType == Shader::UNIFORM_MATRIX && lua_type(L, startidx + 1) == LUA_TSTRING)
|
||||
math::Transform::MatrixLayout layout = math::Transform::MATRIX_ROW_MAJOR;
|
||||
int dataidx = startidx;
|
||||
if (info->baseType == Shader::UNIFORM_MATRIX)
|
||||
{
|
||||
const char *layoutstr = lua_tostring(L, startidx + 1);
|
||||
math::Transform::MatrixLayout layout;
|
||||
if (!math::Transform::getConstant(layoutstr, layout))
|
||||
return luax_enumerror(L, "matrix layout", math::Transform::getConstants(layout), layoutstr);
|
||||
if (lua_type(L, startidx) == LUA_TSTRING)
|
||||
{
|
||||
// (matrixlayout, data, ...)
|
||||
const char *layoutstr = lua_tostring(L, startidx);
|
||||
if (!math::Transform::getConstant(layoutstr, layout))
|
||||
return luax_enumerror(L, "matrix layout", math::Transform::getConstants(layout), layoutstr);
|
||||
|
||||
columnmajor = (layout == math::Transform::MATRIX_COLUMN_MAJOR);
|
||||
startidx++;
|
||||
startidx++;
|
||||
dataidx = startidx;
|
||||
}
|
||||
else if (lua_type(L, startidx + 1) == LUA_TSTRING)
|
||||
{
|
||||
// (data, matrixlayout, ...)
|
||||
// Should be deprecated in the future (doesn't match the argument
|
||||
// order of Shader:send(name, matrixlayout, table))
|
||||
const char *layoutstr = lua_tostring(L, startidx + 1);
|
||||
if (!math::Transform::getConstant(layoutstr, layout))
|
||||
return luax_enumerror(L, "matrix layout", math::Transform::getConstants(layout), layoutstr);
|
||||
|
||||
startidx++;
|
||||
}
|
||||
}
|
||||
|
||||
Data *data = luax_checktype<Data>(L, startidx);
|
||||
bool columnmajor = (layout == math::Transform::MATRIX_COLUMN_MAJOR);
|
||||
|
||||
Data *data = luax_checktype<Data>(L, dataidx);
|
||||
size_t size = data->getSize();
|
||||
|
||||
ptrdiff_t offset = (ptrdiff_t) luaL_optinteger(L, startidx + 1, 0);
|
||||
@@ -339,17 +374,17 @@ static int w_Shader_sendData(lua_State *L, int startidx, Shader *shader, const S
|
||||
|
||||
if (!lua_isnoneornil(L, startidx + 2))
|
||||
{
|
||||
lua_Integer datasize = luaL_checkinteger(L, startidx + 2);
|
||||
if (datasize <= 0)
|
||||
lua_Integer sizearg = luaL_checkinteger(L, startidx + 2);
|
||||
if (sizearg <= 0)
|
||||
return luaL_error(L, "Size must be greater than 0.");
|
||||
else if ((size_t) datasize > size - offset)
|
||||
else if ((size_t) sizearg > size - offset)
|
||||
return luaL_error(L, "Size and offset must fit within the Data's bounds.");
|
||||
else if (size % uniformstride != 0)
|
||||
return luaL_error(L, "Size must be a multiple of the uniform's size in bytes.");
|
||||
else if (size > info->dataSize)
|
||||
else if (sizearg % uniformstride != 0)
|
||||
return luaL_error(L, "Size (%d) must be a multiple of the uniform's size in bytes (%d).", sizearg, uniformstride);
|
||||
else if ((size_t) sizearg > info->dataSize)
|
||||
return luaL_error(L, "Size must not be greater than the uniform's total size in bytes.");
|
||||
|
||||
size = (size_t) datasize;
|
||||
size = (size_t) sizearg;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -416,7 +451,7 @@ int w_Shader_send(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (luax_istype(L, 3, Data::type))
|
||||
if (luax_istype(L, 3, Data::type) || (info->baseType == Shader::UNIFORM_MATRIX && luax_istype(L, 4, Data::type)))
|
||||
w_Shader_sendData(L, 3, shader, info, false);
|
||||
else
|
||||
w_Shader_sendLuaValues(L, 3, shader, info, name);
|
||||
|
||||
@@ -216,9 +216,22 @@ int w_SpriteBatch_attachAttribute(lua_State *L)
|
||||
{
|
||||
SpriteBatch *t = luax_checkspritebatch(L, 1);
|
||||
const char *name = luaL_checkstring(L, 2);
|
||||
Mesh *m = luax_checktype<Mesh>(L, 3);
|
||||
|
||||
luax_catchexcept(L, [&](){ t->attachAttribute(name, m); });
|
||||
Buffer *buffer = nullptr;
|
||||
if (luax_istype(L, 3, Buffer::type))
|
||||
{
|
||||
buffer = luax_checktype<Buffer>(L, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mesh *mesh = luax_checktype<Mesh>(L, 3);
|
||||
buffer = mesh->getVertexBuffer();
|
||||
if (buffer == nullptr)
|
||||
return luaL_error(L, "Mesh does not have its own vertex buffer.");
|
||||
luax_markdeprecated(L, "SpriteBatch:attachAttribute(name, mesh)", API_METHOD, DEPRECATED_REPLACED, "SpriteBatch:attachAttribute(name, buffer)");
|
||||
}
|
||||
|
||||
luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,12 +30,13 @@ static void loveSTBIAssert(bool test, const char *teststr)
|
||||
}
|
||||
|
||||
// stb_image
|
||||
#define STBI_ONLY_JPEG
|
||||
#define STBI_ONLY_JPEG
|
||||
// #define STBI_ONLY_PNG
|
||||
#define STBI_ONLY_BMP
|
||||
#define STBI_ONLY_TGA
|
||||
#define STBI_ONLY_HDR
|
||||
#define STBI_NO_STDIO
|
||||
#define STB_IMAGE_STATIC
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_ASSERT(A) loveSTBIAssert((A), #A)
|
||||
#include "libraries/stb/stb_image.h"
|
||||
|
||||
@@ -36,10 +36,6 @@
|
||||
|
||||
#ifdef LOVE_ANDROID
|
||||
#include <SDL.h>
|
||||
extern "C"
|
||||
{
|
||||
#include "luajit.h"
|
||||
}
|
||||
#endif // LOVE_ANDROID
|
||||
|
||||
#ifdef LOVE_LEGENDARY_CONSOLE_IO_HACK
|
||||
@@ -383,7 +379,6 @@ int luaopen_love(lua_State *L)
|
||||
lua_setfield(L, -2, "_version_codename");
|
||||
|
||||
#ifdef LOVE_ANDROID
|
||||
luaJIT_setmode(L, 0, LUAJIT_MODE_ENGINE | LUAJIT_MODE_OFF);
|
||||
lua_register(L, "print", w_print_sdl_log);
|
||||
#endif
|
||||
|
||||
|
||||
+70
-39
@@ -24,6 +24,7 @@
|
||||
#include "common/delay.h"
|
||||
#include "Timer.h"
|
||||
|
||||
#include <iostream>
|
||||
#if defined(LOVE_WINDOWS)
|
||||
#include <windows.h>
|
||||
#elif defined(LOVE_MACOS) || defined(LOVE_IOS)
|
||||
@@ -35,15 +36,6 @@
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#if defined(LOVE_LINUX)
|
||||
static inline double getTimeOfDay()
|
||||
{
|
||||
timeval t;
|
||||
gettimeofday(&t, NULL);
|
||||
return (double) t.tv_sec + (double) t.tv_usec / 1000000.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace timer
|
||||
@@ -109,52 +101,91 @@ double Timer::getAverageDelta() const
|
||||
return averageDelta;
|
||||
}
|
||||
|
||||
double Timer::getTimerPeriod()
|
||||
#if defined(LOVE_LINUX)
|
||||
|
||||
static inline timespec getTimeOfDay()
|
||||
{
|
||||
#if defined(LOVE_MACOS) || defined(LOVE_IOS)
|
||||
mach_timebase_info_data_t info;
|
||||
mach_timebase_info(&info);
|
||||
return (double) info.numer / (double) info.denom / 1000000000.0;
|
||||
#elif defined(LOVE_WINDOWS)
|
||||
LARGE_INTEGER temp;
|
||||
if (QueryPerformanceFrequency(&temp) != 0 && temp.QuadPart != 0)
|
||||
return 1.0 / (double) temp.QuadPart;
|
||||
#endif
|
||||
return 0;
|
||||
timeval t;
|
||||
gettimeofday(&t, NULL);
|
||||
return timespec { t.tv_sec, t.tv_usec * 1000 };
|
||||
}
|
||||
|
||||
double Timer::getTime()
|
||||
static timespec getTimeAbsolute()
|
||||
{
|
||||
// The timer period (reciprocal of the frequency.)
|
||||
static const double timerPeriod = getTimerPeriod();
|
||||
|
||||
#if defined(LOVE_LINUX)
|
||||
(void) timerPeriod; // Unused on linux
|
||||
|
||||
double mt;
|
||||
// Check for POSIX timers and monotonic clocks. If not supported, use the gettimeofday fallback.
|
||||
#if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK) \
|
||||
&& (defined(CLOCK_MONOTONIC_RAW) || defined(CLOCK_MONOTONIC))
|
||||
timespec t;
|
||||
|
||||
#ifdef CLOCK_MONOTONIC_RAW
|
||||
clockid_t clk_id = CLOCK_MONOTONIC_RAW;
|
||||
#else
|
||||
clockid_t clk_id = CLOCK_MONOTONIC;
|
||||
#endif
|
||||
|
||||
timespec t;
|
||||
if (clock_gettime(clk_id, &t) == 0)
|
||||
mt = (double) t.tv_sec + (double) t.tv_nsec / 1000000000.0;
|
||||
return t;
|
||||
else
|
||||
return getTimeOfDay();
|
||||
#endif
|
||||
mt = getTimeOfDay();
|
||||
return mt;
|
||||
#elif defined(LOVE_MACOS) || defined(LOVE_IOS)
|
||||
return (double) mach_absolute_time() * timerPeriod;
|
||||
#elif defined(LOVE_WINDOWS)
|
||||
LARGE_INTEGER microTime;
|
||||
QueryPerformanceCounter(µTime);
|
||||
return (double) microTime.QuadPart * timerPeriod;
|
||||
#endif
|
||||
return getTimeOfDay();
|
||||
}
|
||||
|
||||
double Timer::getTime()
|
||||
{
|
||||
static const timespec start = getTimeAbsolute();
|
||||
const timespec now = getTimeAbsolute();
|
||||
// tv_sec and tv_nsec should be signed on POSIX, so we are fine in just subtracting here.
|
||||
const long sec = now.tv_sec - start.tv_sec;
|
||||
const long nsec = now.tv_nsec - start.tv_nsec;
|
||||
return (double) sec + (double) nsec / 1.0e9;
|
||||
}
|
||||
|
||||
#elif defined(LOVE_MACOS) || defined(LOVE_IOS)
|
||||
|
||||
static mach_timebase_info_data_t getTimebaseInfo()
|
||||
{
|
||||
mach_timebase_info_data_t info;
|
||||
mach_timebase_info(&info);
|
||||
return info;
|
||||
}
|
||||
|
||||
double Timer::getTime()
|
||||
{
|
||||
static const mach_timebase_info_data_t info = getTimebaseInfo();
|
||||
static const uint64_t start = mach_absolute_time();
|
||||
const uint64_t rel = mach_absolute_time() - start;
|
||||
return ((double) rel * 1.0e-9) * (double) info.numer / (double) info.denom;
|
||||
}
|
||||
|
||||
#elif defined(LOVE_WINDOWS)
|
||||
|
||||
static LARGE_INTEGER getTimeAbsolute()
|
||||
{
|
||||
LARGE_INTEGER t;
|
||||
QueryPerformanceCounter(&t);
|
||||
return t;
|
||||
}
|
||||
|
||||
static LARGE_INTEGER getFrequency()
|
||||
{
|
||||
LARGE_INTEGER freq;
|
||||
// "On systems that run Windows XP or later, the function will always succeed and will thus never return zero."
|
||||
QueryPerformanceFrequency(&freq);
|
||||
return freq;
|
||||
}
|
||||
|
||||
double Timer::getTime()
|
||||
{
|
||||
static const LARGE_INTEGER freq = getFrequency();
|
||||
static const LARGE_INTEGER start = getTimeAbsolute();
|
||||
const LARGE_INTEGER now = getTimeAbsolute();
|
||||
LARGE_INTEGER rel;
|
||||
rel.QuadPart = now.QuadPart - start.QuadPart;
|
||||
return (double) rel.QuadPart / (double) freq.QuadPart;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // timer
|
||||
} // love
|
||||
|
||||
@@ -72,9 +72,11 @@ public:
|
||||
double getAverageDelta() const;
|
||||
|
||||
/**
|
||||
* Gets the amount of time passed since an unspecified time. Useful for
|
||||
* profiling code or measuring intervals. The time is microsecond-precise,
|
||||
* and increases monotonically.
|
||||
* Gets the amount of time in seconds passed since its first invocation
|
||||
* (which happens as part of the Timer constructor,
|
||||
* which is called when the module is first opened).
|
||||
* Useful for profiling code or measuring intervals.
|
||||
* The time is microsecond-precise, and increases monotonically.
|
||||
* @return The time (in seconds)
|
||||
**/
|
||||
static double getTime();
|
||||
@@ -99,9 +101,6 @@ private:
|
||||
// The current timestep.
|
||||
double dt;
|
||||
|
||||
// Returns the timer period on some platforms.
|
||||
static double getTimerPeriod();
|
||||
|
||||
}; // Timer
|
||||
|
||||
} // timer
|
||||
|
||||
@@ -532,6 +532,7 @@ function love.init()
|
||||
|
||||
-- Setup window here.
|
||||
if c.window and c.modules.window then
|
||||
love.window.setTitle(c.window.title or c.title)
|
||||
assert(love.window.setMode(c.window.width, c.window.height,
|
||||
{
|
||||
fullscreen = c.window.fullscreen,
|
||||
@@ -551,7 +552,6 @@ function love.init()
|
||||
x = c.window.x,
|
||||
y = c.window.y,
|
||||
}), "Could not set window mode")
|
||||
love.window.setTitle(c.window.title or c.title)
|
||||
if c.window.icon then
|
||||
assert(love.image, "If an icon is set in love.conf, love.image must be loaded!")
|
||||
love.window.setIcon(love.image.newImageData(c.window.icon))
|
||||
|
||||
@@ -1055,6 +1055,9 @@ const unsigned char boot_lua[] =
|
||||
0x09, 0x69, 0x66, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x63,
|
||||
0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x74, 0x68,
|
||||
0x65, 0x6e, 0x0a,
|
||||
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x73, 0x65, 0x74, 0x54,
|
||||
0x69, 0x74, 0x6c, 0x65, 0x28, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x74, 0x69, 0x74, 0x6c,
|
||||
0x65, 0x20, 0x6f, 0x72, 0x20, 0x63, 0x2e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x29, 0x0a,
|
||||
0x09, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64,
|
||||
0x6f, 0x77, 0x2e, 0x73, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x28, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f,
|
||||
0x77, 0x2e, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e,
|
||||
@@ -1094,9 +1097,6 @@ const unsigned char boot_lua[] =
|
||||
0x09, 0x09, 0x09, 0x79, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x79, 0x2c, 0x0a,
|
||||
0x09, 0x09, 0x7d, 0x29, 0x2c, 0x20, 0x22, 0x43, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73,
|
||||
0x65, 0x74, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x29, 0x0a,
|
||||
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x73, 0x65, 0x74, 0x54,
|
||||
0x69, 0x74, 0x6c, 0x65, 0x28, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x74, 0x69, 0x74, 0x6c,
|
||||
0x65, 0x20, 0x6f, 0x72, 0x20, 0x63, 0x2e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x29, 0x0a,
|
||||
0x09, 0x09, 0x69, 0x66, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x69, 0x63, 0x6f, 0x6e,
|
||||
0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x69, 0x6d, 0x61,
|
||||
|
||||
Reference in New Issue
Block a user