mirror of https://github.com/bjornbytes/lovr.git
Start sized int conversions;
This commit is contained in:
parent
c31853a1d4
commit
57aad490ae
|
@ -50,8 +50,8 @@ void luax_readattachments(lua_State* L, int index, Attachment* attachments, int*
|
|||
|
||||
static int l_lovrCanvasNewTextureData(lua_State* L) {
|
||||
Canvas* canvas = luax_checktype(L, 1, Canvas);
|
||||
int index = luaL_optinteger(L, 2, 1) - 1;
|
||||
int count;
|
||||
uint32_t index = luaL_optinteger(L, 2, 1) - 1;
|
||||
uint32_t count;
|
||||
lovrCanvasGetAttachments(canvas, &count);
|
||||
lovrAssert(index >= 0 && index < count, "Can not create a TextureData from Texture #%d of Canvas (it only has %d textures)", index, count);
|
||||
TextureData* textureData = lovrCanvasNewTextureData(canvas, index);
|
||||
|
@ -73,9 +73,9 @@ static int l_lovrCanvasRenderTo(lua_State* L) {
|
|||
|
||||
static int l_lovrCanvasGetTexture(lua_State* L) {
|
||||
Canvas* canvas = luax_checktype(L, 1, Canvas);
|
||||
int count;
|
||||
uint32_t count;
|
||||
const Attachment* attachments = lovrCanvasGetAttachments(canvas, &count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
luax_pushobject(L, attachments[i].texture);
|
||||
}
|
||||
return count;
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
#include "graphics/texture.h"
|
||||
|
||||
int luax_optmipmap(lua_State* L, int index, Texture* texture) {
|
||||
int mipmap = luaL_optinteger(L, index, 1);
|
||||
lovrAssert(mipmap > 0 && mipmap <= lovrTextureGetMipmapCount(texture), "Invalid mipmap %d\n", mipmap);
|
||||
uint32_t mipmap = luaL_optinteger(L, index, 1);
|
||||
lovrAssert(mipmap <= lovrTextureGetMipmapCount(texture), "Invalid mipmap %d\n", mipmap);
|
||||
return mipmap - 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ static struct {
|
|||
float velocity[3];
|
||||
} state;
|
||||
|
||||
ALenum lovrAudioConvertFormat(int bitDepth, int channelCount) {
|
||||
ALenum lovrAudioConvertFormat(uint32_t bitDepth, uint32_t channelCount) {
|
||||
if (bitDepth == 8 && channelCount == 1) {
|
||||
return AL_FORMAT_MONO8;
|
||||
} else if (bitDepth == 8 && channelCount == 2) {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
struct Source;
|
||||
|
||||
int lovrAudioConvertFormat(int bitDepth, int channelCount);
|
||||
int lovrAudioConvertFormat(uint32_t bitDepth, uint32_t channelCount);
|
||||
|
||||
bool lovrAudioInit(void);
|
||||
void lovrAudioDestroy(void);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "util.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
AudioStream* lovrAudioStreamInit(AudioStream* stream, Blob* blob, int bufferSize) {
|
||||
AudioStream* lovrAudioStreamInit(AudioStream* stream, Blob* blob, size_t bufferSize) {
|
||||
stb_vorbis* decoder = stb_vorbis_open_memory(blob->data, (int) blob->size, NULL, NULL);
|
||||
lovrAssert(decoder, "Could not create audio stream for '%s'", blob->name);
|
||||
|
||||
|
@ -15,12 +15,11 @@ AudioStream* lovrAudioStreamInit(AudioStream* stream, Blob* blob, int bufferSize
|
|||
stream->sampleRate = info.sample_rate;
|
||||
stream->samples = stb_vorbis_stream_length_in_samples(decoder);
|
||||
stream->decoder = decoder;
|
||||
stream->bufferSize = stream->channelCount * bufferSize * sizeof(short);
|
||||
stream->bufferSize = stream->channelCount * bufferSize * sizeof(int16_t);
|
||||
stream->buffer = malloc(stream->bufferSize);
|
||||
lovrAssert(stream->buffer, "Out of memory");
|
||||
stream->blob = blob;
|
||||
lovrRetain(blob);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
@ -31,12 +30,12 @@ void lovrAudioStreamDestroy(void* ref) {
|
|||
free(stream->buffer);
|
||||
}
|
||||
|
||||
int lovrAudioStreamDecode(AudioStream* stream, short* destination, int size) {
|
||||
size_t lovrAudioStreamDecode(AudioStream* stream, int16_t* destination, size_t size) {
|
||||
stb_vorbis* decoder = (stb_vorbis*) stream->decoder;
|
||||
short* buffer = destination ? destination : (short*) stream->buffer;
|
||||
int capacity = destination ? size : (stream->bufferSize / sizeof(short));
|
||||
int channelCount = stream->channelCount;
|
||||
int samples = 0;
|
||||
int16_t* buffer = destination ? destination : (int16_t*) stream->buffer;
|
||||
size_t capacity = destination ? size : (stream->bufferSize / sizeof(int16_t));
|
||||
uint32_t channelCount = stream->channelCount;
|
||||
size_t samples = 0;
|
||||
|
||||
while (samples < capacity) {
|
||||
int count = stb_vorbis_get_samples_short_interleaved(decoder, channelCount, buffer + samples, capacity - samples);
|
||||
|
@ -52,12 +51,12 @@ void lovrAudioStreamRewind(AudioStream* stream) {
|
|||
stb_vorbis_seek_start(decoder);
|
||||
}
|
||||
|
||||
void lovrAudioStreamSeek(AudioStream* stream, int sample) {
|
||||
void lovrAudioStreamSeek(AudioStream* stream, size_t sample) {
|
||||
stb_vorbis* decoder = (stb_vorbis*) stream->decoder;
|
||||
stb_vorbis_seek(decoder, sample);
|
||||
}
|
||||
|
||||
int lovrAudioStreamTell(AudioStream* stream) {
|
||||
size_t lovrAudioStreamTell(AudioStream* stream) {
|
||||
stb_vorbis* decoder = (stb_vorbis*) stream->decoder;
|
||||
return stb_vorbis_get_sample_offset(decoder);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#include "types.h"
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
|
@ -6,20 +8,20 @@ struct Blob;
|
|||
|
||||
typedef struct AudioStream {
|
||||
Ref ref;
|
||||
int bitDepth;
|
||||
int channelCount;
|
||||
int sampleRate;
|
||||
int samples;
|
||||
int bufferSize;
|
||||
uint32_t bitDepth;
|
||||
uint32_t channelCount;
|
||||
uint32_t sampleRate;
|
||||
size_t samples;
|
||||
size_t bufferSize;
|
||||
void* buffer;
|
||||
void* decoder;
|
||||
struct Blob* blob;
|
||||
} AudioStream;
|
||||
|
||||
AudioStream* lovrAudioStreamInit(AudioStream* stream, struct Blob* blob, int bufferSize);
|
||||
AudioStream* lovrAudioStreamInit(AudioStream* stream, struct Blob* blob, size_t bufferSize);
|
||||
#define lovrAudioStreamCreate(...) lovrAudioStreamInit(lovrAlloc(AudioStream), __VA_ARGS__)
|
||||
void lovrAudioStreamDestroy(void* ref);
|
||||
int lovrAudioStreamDecode(AudioStream* stream, short* destination, int size);
|
||||
size_t lovrAudioStreamDecode(AudioStream* stream, int16_t* destination, size_t size);
|
||||
void lovrAudioStreamRewind(AudioStream* stream);
|
||||
void lovrAudioStreamSeek(AudioStream* stream, int sample);
|
||||
int lovrAudioStreamTell(AudioStream* stream);
|
||||
void lovrAudioStreamSeek(AudioStream* stream, size_t sample);
|
||||
size_t lovrAudioStreamTell(AudioStream* stream);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#define FOUR_CC(a, b, c, d) ((uint32_t) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a)))
|
||||
|
||||
static int getPixelSize(TextureFormat format) {
|
||||
static size_t getPixelSize(TextureFormat format) {
|
||||
switch (format) {
|
||||
case FORMAT_RGB: return 3;
|
||||
case FORMAT_RGBA: return 4;
|
||||
|
@ -30,9 +30,9 @@ static int getPixelSize(TextureFormat format) {
|
|||
}
|
||||
|
||||
// Modified from ddsparse (https://bitbucket.org/slime73/ddsparse)
|
||||
static int parseDDS(uint8_t* data, size_t size, TextureData* textureData) {
|
||||
static bool parseDDS(uint8_t* data, size_t size, TextureData* textureData) {
|
||||
if (size < sizeof(uint32_t) + sizeof(DDSHeader) || *(uint32_t*) data != FOUR_CC('D', 'D', 'S', ' ')) {
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Header
|
||||
|
@ -40,13 +40,13 @@ static int parseDDS(uint8_t* data, size_t size, TextureData* textureData) {
|
|||
DDSHeader* header = (DDSHeader*) (data + offset);
|
||||
offset += sizeof(DDSHeader);
|
||||
if (header->size != sizeof(DDSHeader) || header->format.size != sizeof(DDSPixelFormat)) {
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// DX10 header
|
||||
if ((header->format.flags & DDPF_FOURCC) && (header->format.fourCC == FOUR_CC('D', 'X', '1', '0'))) {
|
||||
if (size < (sizeof(uint32_t) + sizeof(DDSHeader) + sizeof(DDSHeader10))) {
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
DDSHeader10* header10 = (DDSHeader10*) (data + offset);
|
||||
|
@ -55,12 +55,12 @@ static int parseDDS(uint8_t* data, size_t size, TextureData* textureData) {
|
|||
// Only accept 2D textures
|
||||
D3D10ResourceDimension dimension = header10->resourceDimension;
|
||||
if (dimension != D3D10_RESOURCE_DIMENSION_TEXTURE2D && dimension != D3D10_RESOURCE_DIMENSION_UNKNOWN) {
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Can't deal with texture arrays and cubemaps.
|
||||
if (header10->arraySize > 1) {
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure DXT 1/3/5
|
||||
|
@ -85,7 +85,7 @@ static int parseDDS(uint8_t* data, size_t size, TextureData* textureData) {
|
|||
}
|
||||
} else {
|
||||
if ((header->format.flags & DDPF_FOURCC) == 0) {
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure DXT 1/3/5
|
||||
|
@ -93,14 +93,14 @@ static int parseDDS(uint8_t* data, size_t size, TextureData* textureData) {
|
|||
case FOUR_CC('D', 'X', 'T', '1'): textureData->format = FORMAT_DXT1; break;
|
||||
case FOUR_CC('D', 'X', 'T', '3'): textureData->format = FORMAT_DXT3; break;
|
||||
case FOUR_CC('D', 'X', 'T', '5'): textureData->format = FORMAT_DXT5; break;
|
||||
default: return 1;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
int width = textureData->width = header->width;
|
||||
int height = textureData->height = header->height;
|
||||
int mipmapCount = MAX(header->mipMapCount, 1);
|
||||
int blockBytes = 0;
|
||||
uint32_t width = textureData->width = header->width;
|
||||
uint32_t height = textureData->height = header->height;
|
||||
uint32_t mipmapCount = MAX(header->mipMapCount, 1);
|
||||
size_t blockBytes = 0;
|
||||
|
||||
switch (textureData->format) {
|
||||
case FORMAT_DXT1: blockBytes = 8; break;
|
||||
|
@ -110,15 +110,15 @@ static int parseDDS(uint8_t* data, size_t size, TextureData* textureData) {
|
|||
}
|
||||
|
||||
// Load mipmaps
|
||||
for (int i = 0; i < mipmapCount; i++) {
|
||||
int numBlocksWide = width ? MAX(1, (width + 3) / 4) : 0;
|
||||
int numBlocksHigh = height ? MAX(1, (height + 3) / 4) : 0;
|
||||
int mipmapSize = numBlocksWide * numBlocksHigh * blockBytes;
|
||||
for (uint32_t i = 0; i < mipmapCount; i++) {
|
||||
uint32_t numBlocksWide = width ? MAX(1, (width + 3) / 4) : 0;
|
||||
uint32_t numBlocksHigh = height ? MAX(1, (height + 3) / 4) : 0;
|
||||
size_t mipmapSize = numBlocksWide * numBlocksHigh * blockBytes;
|
||||
|
||||
// Overflow check
|
||||
if (mipmapSize == 0 || (offset + mipmapSize) > size) {
|
||||
vec_deinit(&textureData->mipmaps);
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
Mipmap mipmap = { .width = width, .height = height, .data = &data[offset], .size = mipmapSize };
|
||||
|
@ -130,10 +130,10 @@ static int parseDDS(uint8_t* data, size_t size, TextureData* textureData) {
|
|||
|
||||
textureData->blob.data = NULL;
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int parseKTX(uint8_t* d, size_t size, TextureData* textureData) {
|
||||
static bool parseKTX(uint8_t* d, size_t size, TextureData* textureData) {
|
||||
union {
|
||||
uint8_t* u8;
|
||||
uint32_t* u32;
|
||||
|
@ -143,11 +143,11 @@ static int parseKTX(uint8_t* d, size_t size, TextureData* textureData) {
|
|||
uint8_t magic[] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
|
||||
|
||||
if (size < sizeof(magic) || memcmp(data.ktx->header, magic, sizeof(magic))) {
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data.ktx->endianness != 0x04030201 || data.ktx->numberOfArrayElements > 0 || data.ktx->numberOfFaces > 1 || data.ktx->pixelDepth > 1) {
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO RGBA DXT1, SRGB DXT formats
|
||||
|
@ -155,7 +155,7 @@ static int parseKTX(uint8_t* d, size_t size, TextureData* textureData) {
|
|||
case 0x83F0: textureData->format = FORMAT_DXT1; break;
|
||||
case 0x83F2: textureData->format = FORMAT_DXT3; break;
|
||||
case 0x83F3: textureData->format = FORMAT_DXT5; break;
|
||||
default: return 1;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
uint32_t width = textureData->width = data.ktx->pixelWidth;
|
||||
|
@ -177,10 +177,10 @@ static int parseKTX(uint8_t* d, size_t size, TextureData* textureData) {
|
|||
data.u8 = (uint8_t*) ALIGN(data.u8 + sizeof(uint32_t) + *data.u32 + 3, 4);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
TextureData* lovrTextureDataInit(TextureData* textureData, int width, int height, uint8_t value, TextureFormat format) {
|
||||
TextureData* lovrTextureDataInit(TextureData* textureData, uint32_t width, uint32_t height, uint8_t value, TextureFormat format) {
|
||||
lovrAssert(width > 0 && height > 0, "TextureData dimensions must be positive");
|
||||
lovrAssert(format != FORMAT_DXT1 && format != FORMAT_DXT3 && format != FORMAT_DXT5, "Blank TextureData cannot be compressed");
|
||||
size_t pixelSize = getPixelSize(format);
|
||||
|
@ -199,24 +199,25 @@ TextureData* lovrTextureDataInit(TextureData* textureData, int width, int height
|
|||
TextureData* lovrTextureDataInitFromBlob(TextureData* textureData, Blob* blob, bool flip) {
|
||||
vec_init(&textureData->mipmaps);
|
||||
|
||||
if (!parseDDS(blob->data, blob->size, textureData)) {
|
||||
if (parseDDS(blob->data, blob->size, textureData)) {
|
||||
textureData->source = blob;
|
||||
lovrRetain(blob);
|
||||
return textureData;
|
||||
} else if (!parseKTX(blob->data, blob->size, textureData)) {
|
||||
} else if (parseKTX(blob->data, blob->size, textureData)) {
|
||||
textureData->source = blob;
|
||||
lovrRetain(blob);
|
||||
return textureData;
|
||||
}
|
||||
|
||||
int width, height;
|
||||
int length = (int) blob->size;
|
||||
stbi_set_flip_vertically_on_load(flip);
|
||||
if (stbi_is_hdr_from_memory(blob->data, length)) {
|
||||
textureData->format = FORMAT_RGBA32F;
|
||||
textureData->blob.data = stbi_loadf_from_memory(blob->data, length, &textureData->width, &textureData->height, NULL, 4);
|
||||
textureData->blob.data = stbi_loadf_from_memory(blob->data, length, &width, &height, NULL, 4);
|
||||
} else {
|
||||
textureData->format = FORMAT_RGBA;
|
||||
textureData->blob.data = stbi_load_from_memory(blob->data, length, &textureData->width, &textureData->height, NULL, 4);
|
||||
textureData->blob.data = stbi_load_from_memory(blob->data, length, &width, &height, NULL, 4);
|
||||
}
|
||||
|
||||
if (!textureData->blob.data) {
|
||||
|
@ -225,14 +226,16 @@ TextureData* lovrTextureDataInitFromBlob(TextureData* textureData, Blob* blob, b
|
|||
return NULL;
|
||||
}
|
||||
|
||||
textureData->width = width;
|
||||
textureData->height = height;
|
||||
return textureData;
|
||||
}
|
||||
|
||||
Color lovrTextureDataGetPixel(TextureData* textureData, int x, int y) {
|
||||
Color lovrTextureDataGetPixel(TextureData* textureData, uint32_t x, uint32_t y) {
|
||||
lovrAssert(textureData->blob.data, "TextureData does not have any pixel data");
|
||||
lovrAssert(x >= 0 && y >= 0 && x < textureData->width && y < textureData->height, "getPixel coordinates must be within TextureData bounds");
|
||||
int index = (textureData->height - (y + 1)) * textureData->width + x;
|
||||
int pixelSize = getPixelSize(textureData->format);
|
||||
lovrAssert(x < textureData->width && y < textureData->height, "getPixel coordinates must be within TextureData bounds");
|
||||
size_t index = (textureData->height - (y + 1)) * textureData->width + x;
|
||||
size_t pixelSize = getPixelSize(textureData->format);
|
||||
uint8_t* u8 = (uint8_t*) textureData->blob.data + pixelSize * index;
|
||||
float* f32 = (float*) u8;
|
||||
switch (textureData->format) {
|
||||
|
@ -245,11 +248,11 @@ Color lovrTextureDataGetPixel(TextureData* textureData, int x, int y) {
|
|||
}
|
||||
}
|
||||
|
||||
void lovrTextureDataSetPixel(TextureData* textureData, int x, int y, Color color) {
|
||||
void lovrTextureDataSetPixel(TextureData* textureData, uint32_t x, uint32_t y, Color color) {
|
||||
lovrAssert(textureData->blob.data, "TextureData does not have any pixel data");
|
||||
lovrAssert(x >= 0 && y >= 0 && x < textureData->width && y < textureData->height, "setPixel coordinates must be within TextureData bounds");
|
||||
int index = (textureData->height - (y + 1)) * textureData->width + x;
|
||||
int pixelSize = getPixelSize(textureData->format);
|
||||
lovrAssert(x < textureData->width && y < textureData->height, "setPixel coordinates must be within TextureData bounds");
|
||||
size_t index = (textureData->height - (y + 1)) * textureData->width + x;
|
||||
size_t pixelSize = getPixelSize(textureData->format);
|
||||
uint8_t* u8 = (uint8_t*) textureData->blob.data + pixelSize * index;
|
||||
float* f32 = (float*) u8;
|
||||
switch (textureData->format) {
|
||||
|
|
|
@ -29,29 +29,29 @@ typedef enum {
|
|||
} TextureFormat;
|
||||
|
||||
typedef struct {
|
||||
int width;
|
||||
int height;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
size_t size;
|
||||
void* data;
|
||||
int size;
|
||||
} Mipmap;
|
||||
|
||||
typedef vec_t(Mipmap) vec_mipmap_t;
|
||||
|
||||
typedef struct TextureData {
|
||||
Blob blob;
|
||||
int width;
|
||||
int height;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
Blob* source;
|
||||
TextureFormat format;
|
||||
vec_mipmap_t mipmaps;
|
||||
} TextureData;
|
||||
|
||||
TextureData* lovrTextureDataInit(TextureData* textureData, int width, int height, uint8_t value, TextureFormat format);
|
||||
TextureData* lovrTextureDataInit(TextureData* textureData, uint32_t width, uint32_t height, uint8_t value, TextureFormat format);
|
||||
TextureData* lovrTextureDataInitFromBlob(TextureData* textureData, Blob* blob, bool flip);
|
||||
#define lovrTextureDataCreate(...) lovrTextureDataInit(lovrAlloc(TextureData), __VA_ARGS__)
|
||||
#define lovrTextureDataCreateFromBlob(...) lovrTextureDataInitFromBlob(lovrAlloc(TextureData), __VA_ARGS__)
|
||||
Color lovrTextureDataGetPixel(TextureData* textureData, int x, int y);
|
||||
void lovrTextureDataSetPixel(TextureData* textureData, int x, int y, Color color);
|
||||
Color lovrTextureDataGetPixel(TextureData* textureData, uint32_t x, uint32_t y);
|
||||
void lovrTextureDataSetPixel(TextureData* textureData, uint32_t x, uint32_t y, Color color);
|
||||
bool lovrTextureDataEncode(TextureData* textureData, const char* filename);
|
||||
void lovrTextureDataDestroy(void* ref);
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#include "graphics/canvas.h"
|
||||
#include "graphics/graphics.h"
|
||||
|
||||
const Attachment* lovrCanvasGetAttachments(Canvas* canvas, int* count) {
|
||||
const Attachment* lovrCanvasGetAttachments(Canvas* canvas, uint32_t* count) {
|
||||
if (count) *count = canvas->attachmentCount;
|
||||
return canvas->attachments;
|
||||
}
|
||||
|
||||
void lovrCanvasSetAttachments(Canvas* canvas, Attachment* attachments, int count) {
|
||||
void lovrCanvasSetAttachments(Canvas* canvas, Attachment* attachments, uint32_t count) {
|
||||
lovrAssert(count > 0, "A Canvas must have at least one attached Texture");
|
||||
lovrAssert(count <= MAX_CANVAS_ATTACHMENTS, "Only %d textures can be attached to a Canvas, got %d\n", MAX_CANVAS_ATTACHMENTS, count);
|
||||
|
||||
|
@ -16,24 +16,24 @@ void lovrCanvasSetAttachments(Canvas* canvas, Attachment* attachments, int count
|
|||
|
||||
lovrGraphicsFlushCanvas(canvas);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
Texture* texture = attachments[i].texture;
|
||||
int slice = attachments[i].slice;
|
||||
int level = attachments[i].level;
|
||||
int width = lovrTextureGetWidth(texture, level);
|
||||
int height = lovrTextureGetHeight(texture, level);
|
||||
int depth = lovrTextureGetDepth(texture, level);
|
||||
int mipmaps = lovrTextureGetMipmapCount(texture);
|
||||
uint32_t slice = attachments[i].slice;
|
||||
uint32_t level = attachments[i].level;
|
||||
uint32_t width = lovrTextureGetWidth(texture, level);
|
||||
uint32_t height = lovrTextureGetHeight(texture, level);
|
||||
uint32_t depth = lovrTextureGetDepth(texture, level);
|
||||
uint32_t mipmaps = lovrTextureGetMipmapCount(texture);
|
||||
bool hasDepthBuffer = canvas->flags.depth.enabled;
|
||||
lovrAssert(slice >= 0 && slice < depth, "Invalid attachment slice (Texture has %d, got %d)", depth, slice + 1);
|
||||
lovrAssert(level >= 0 && level < mipmaps, "Invalid attachment mipmap level (Texture has %d, got %d)", mipmaps, level + 1);
|
||||
lovrAssert(slice < depth, "Invalid attachment slice (Texture has %d, got %d)", depth, slice + 1);
|
||||
lovrAssert(level < mipmaps, "Invalid attachment mipmap level (Texture has %d, got %d)", mipmaps, level + 1);
|
||||
lovrAssert(!hasDepthBuffer || width == canvas->width, "Texture width of %d does not match Canvas width (%d)", width, canvas->width);
|
||||
lovrAssert(!hasDepthBuffer || height == canvas->height, "Texture height of %d does not match Canvas height (%d)", height, canvas->height);
|
||||
lovrAssert(texture->msaa == canvas->flags.msaa, "Texture MSAA does not match Canvas MSAA");
|
||||
lovrRetain(texture);
|
||||
}
|
||||
|
||||
for (int i = 0; i < canvas->attachmentCount; i++) {
|
||||
for (uint32_t i = 0; i < canvas->attachmentCount; i++) {
|
||||
lovrRelease(Texture, canvas->attachments[i].texture);
|
||||
}
|
||||
|
||||
|
@ -46,15 +46,15 @@ bool lovrCanvasIsStereo(Canvas* canvas) {
|
|||
return canvas->flags.stereo;
|
||||
}
|
||||
|
||||
int lovrCanvasGetWidth(Canvas* canvas) {
|
||||
uint32_t lovrCanvasGetWidth(Canvas* canvas) {
|
||||
return canvas->width;
|
||||
}
|
||||
|
||||
int lovrCanvasGetHeight(Canvas* canvas) {
|
||||
uint32_t lovrCanvasGetHeight(Canvas* canvas) {
|
||||
return canvas->height;
|
||||
}
|
||||
|
||||
int lovrCanvasGetMSAA(Canvas* canvas) {
|
||||
uint32_t lovrCanvasGetMSAA(Canvas* canvas) {
|
||||
return canvas->flags.msaa;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ struct TextureData;
|
|||
|
||||
typedef struct Attachment {
|
||||
struct Texture* texture;
|
||||
int slice;
|
||||
int level;
|
||||
uint32_t slice;
|
||||
uint32_t level;
|
||||
} Attachment;
|
||||
|
||||
typedef struct {
|
||||
|
@ -21,34 +21,34 @@ typedef struct {
|
|||
TextureFormat format;
|
||||
} depth;
|
||||
bool stereo;
|
||||
int msaa;
|
||||
uint32_t msaa;
|
||||
bool mipmaps;
|
||||
} CanvasFlags;
|
||||
|
||||
typedef struct Canvas {
|
||||
Ref ref;
|
||||
int width;
|
||||
int height;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
CanvasFlags flags;
|
||||
Attachment attachments[MAX_CANVAS_ATTACHMENTS];
|
||||
Attachment depth;
|
||||
int attachmentCount;
|
||||
uint32_t attachmentCount;
|
||||
bool needsAttach;
|
||||
bool needsResolve;
|
||||
GPU_CANVAS_FIELDS
|
||||
} Canvas;
|
||||
|
||||
Canvas* lovrCanvasInit(Canvas* canvas, int width, int height, CanvasFlags flags);
|
||||
Canvas* lovrCanvasInitFromHandle(Canvas* canvas, int width, int height, CanvasFlags flags, uint32_t framebuffer, uint32_t depthBuffer, uint32_t resolveBuffer, int attachmentCount, bool immortal);
|
||||
Canvas* lovrCanvasInit(Canvas* canvas, uint32_t width, uint32_t height, CanvasFlags flags);
|
||||
Canvas* lovrCanvasInitFromHandle(Canvas* canvas, uint32_t width, uint32_t height, CanvasFlags flags, uint32_t framebuffer, uint32_t depthBuffer, uint32_t resolveBuffer, uint32_t attachmentCount, bool immortal);
|
||||
#define lovrCanvasCreate(...) lovrCanvasInit(lovrAlloc(Canvas), __VA_ARGS__)
|
||||
#define lovrCanvasCreateFromHandle(...) lovrCanvasInitFromHandle(lovrAlloc(Canvas), __VA_ARGS__)
|
||||
void lovrCanvasDestroy(void* ref);
|
||||
const Attachment* lovrCanvasGetAttachments(Canvas* canvas, int* count);
|
||||
void lovrCanvasSetAttachments(Canvas* canvas, Attachment* attachments, int count);
|
||||
const Attachment* lovrCanvasGetAttachments(Canvas* canvas, uint32_t* count);
|
||||
void lovrCanvasSetAttachments(Canvas* canvas, Attachment* attachments, uint32_t count);
|
||||
void lovrCanvasResolve(Canvas* canvas);
|
||||
bool lovrCanvasIsStereo(Canvas* canvas);
|
||||
int lovrCanvasGetWidth(Canvas* canvas);
|
||||
int lovrCanvasGetHeight(Canvas* canvas);
|
||||
int lovrCanvasGetMSAA(Canvas* canvas);
|
||||
uint32_t lovrCanvasGetWidth(Canvas* canvas);
|
||||
uint32_t lovrCanvasGetHeight(Canvas* canvas);
|
||||
uint32_t lovrCanvasGetMSAA(Canvas* canvas);
|
||||
struct Texture* lovrCanvasGetDepthTexture(Canvas* canvas);
|
||||
struct TextureData* lovrCanvasNewTextureData(Canvas* canvas, int index);
|
||||
struct TextureData* lovrCanvasNewTextureData(Canvas* canvas, uint32_t index);
|
||||
|
|
|
@ -474,8 +474,8 @@ static void lovrGpuBindImage(Image* image, int slot) {
|
|||
lovrAssert(!texture->srgb, "sRGB textures can not be used as image uniforms");
|
||||
lovrAssert(!isTextureFormatCompressed(texture->format), "Compressed textures can not be used as image uniforms");
|
||||
lovrAssert(texture->format != FORMAT_RGB && texture->format != FORMAT_RGBA4 && texture->format != FORMAT_RGB5A1, "Unsupported texture format for image uniform");
|
||||
lovrAssert(image->mipmap >= 0 && image->mipmap < texture->mipmapCount, "Invalid mipmap level '%d' for image uniform", image->mipmap);
|
||||
lovrAssert(image->slice < texture->depth, "Invalid texture slice '%d' for image uniform", image->slice);
|
||||
lovrAssert(image->mipmap < (int) texture->mipmapCount, "Invalid mipmap level '%d' for image uniform", image->mipmap);
|
||||
lovrAssert(image->slice < (int) texture->depth, "Invalid texture slice '%d' for image uniform", image->slice);
|
||||
GLenum glAccess = convertAccess(image->access);
|
||||
GLenum glFormat = convertTextureFormatInternal(texture->format, false);
|
||||
bool layered = image->slice == -1;
|
||||
|
@ -566,7 +566,7 @@ static void lovrGpuBindCanvas(Canvas* canvas, bool willDraw) {
|
|||
|
||||
// We need to synchronize if any of the Canvas attachments have pending writes on them
|
||||
#ifndef LOVR_WEBGL
|
||||
for (int i = 0; i < canvas->attachmentCount; i++) {
|
||||
for (uint32_t i = 0; i < canvas->attachmentCount; i++) {
|
||||
Texture* texture = canvas->attachments[i].texture;
|
||||
if (texture->incoherent && (texture->incoherent >> BARRIER_CANVAS) & 1) {
|
||||
lovrGpuSync(1 << BARRIER_CANVAS);
|
||||
|
@ -581,12 +581,12 @@ static void lovrGpuBindCanvas(Canvas* canvas, bool willDraw) {
|
|||
}
|
||||
|
||||
GLenum buffers[MAX_CANVAS_ATTACHMENTS] = { GL_NONE };
|
||||
for (int i = 0; i < canvas->attachmentCount; i++) {
|
||||
for (uint32_t i = 0; i < canvas->attachmentCount; i++) {
|
||||
GLenum buffer = buffers[i] = GL_COLOR_ATTACHMENT0 + i;
|
||||
Attachment* attachment = &canvas->attachments[i];
|
||||
Texture* texture = attachment->texture;
|
||||
int slice = attachment->slice;
|
||||
int level = attachment->level;
|
||||
uint32_t slice = attachment->slice;
|
||||
uint32_t level = attachment->level;
|
||||
|
||||
if (canvas->flags.msaa) {
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, buffer, GL_RENDERBUFFER, texture->msaaId);
|
||||
|
@ -1192,7 +1192,7 @@ const GpuStats* lovrGpuGetStats() {
|
|||
|
||||
// Texture
|
||||
|
||||
Texture* lovrTextureInit(Texture* texture, TextureType type, TextureData** slices, int sliceCount, bool srgb, bool mipmaps, int msaa) {
|
||||
Texture* lovrTextureInit(Texture* texture, TextureType type, TextureData** slices, uint32_t sliceCount, bool srgb, bool mipmaps, uint32_t msaa) {
|
||||
texture->type = type;
|
||||
texture->srgb = srgb;
|
||||
texture->mipmaps = mipmaps;
|
||||
|
@ -1210,7 +1210,7 @@ Texture* lovrTextureInit(Texture* texture, TextureType type, TextureData** slice
|
|||
|
||||
if (sliceCount > 0) {
|
||||
lovrTextureAllocate(texture, slices[0]->width, slices[0]->height, sliceCount, slices[0]->format);
|
||||
for (int i = 0; i < sliceCount; i++) {
|
||||
for (uint32_t i = 0; i < sliceCount; i++) {
|
||||
lovrTextureReplacePixels(texture, slices[i], 0, 0, i, 0);
|
||||
}
|
||||
}
|
||||
|
@ -1223,9 +1223,12 @@ Texture* lovrTextureInitFromHandle(Texture* texture, uint32_t handle, TextureTyp
|
|||
texture->id = handle;
|
||||
texture->target = convertTextureTarget(type);
|
||||
|
||||
int width, height;
|
||||
lovrGpuBindTexture(texture, 0);
|
||||
glGetTexLevelParameteriv(texture->target, 0, GL_TEXTURE_WIDTH, &texture->width);
|
||||
glGetTexLevelParameteriv(texture->target, 0, GL_TEXTURE_HEIGHT, &texture->height);
|
||||
glGetTexLevelParameteriv(texture->target, 0, GL_TEXTURE_WIDTH, &width);
|
||||
glGetTexLevelParameteriv(texture->target, 0, GL_TEXTURE_HEIGHT, &height);
|
||||
texture->width = (uint32_t) width;
|
||||
texture->height = (uint32_t) height;
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
@ -1237,8 +1240,8 @@ void lovrTextureDestroy(void* ref) {
|
|||
lovrGpuDestroySyncResource(texture, texture->incoherent);
|
||||
}
|
||||
|
||||
void lovrTextureAllocate(Texture* texture, int width, int height, int depth, TextureFormat format) {
|
||||
int maxSize = state.limits.textureSize;
|
||||
void lovrTextureAllocate(Texture* texture, uint32_t width, uint32_t height, uint32_t depth, TextureFormat format) {
|
||||
uint32_t maxSize = (uint32_t) state.limits.textureSize;
|
||||
lovrAssert(!texture->allocated, "Texture is already allocated");
|
||||
lovrAssert(texture->type != TEXTURE_CUBE || width == height, "Cubemap images must be square");
|
||||
lovrAssert(texture->type != TEXTURE_CUBE || depth == 6, "6 images are required for a cube texture\n");
|
||||
|
@ -1254,7 +1257,7 @@ void lovrTextureAllocate(Texture* texture, int width, int height, int depth, Tex
|
|||
texture->format = format;
|
||||
|
||||
if (texture->mipmaps) {
|
||||
int dimension = texture->type == TEXTURE_VOLUME ? (MAX(MAX(width, height), depth)) : MAX(width, height);
|
||||
uint32_t dimension = texture->type == TEXTURE_VOLUME ? (MAX(MAX(width, height), depth)) : MAX(width, height);
|
||||
texture->mipmapCount = texture->mipmaps ? (log2(dimension) + 1) : 1;
|
||||
} else {
|
||||
texture->mipmapCount = 1;
|
||||
|
@ -1276,14 +1279,14 @@ void lovrTextureAllocate(Texture* texture, int width, int height, int depth, Tex
|
|||
}
|
||||
#ifndef LOVR_WEBGL
|
||||
} else {
|
||||
for (int i = 0; i < texture->mipmapCount; i++) {
|
||||
for (uint32_t i = 0; i < texture->mipmapCount; i++) {
|
||||
switch (texture->type) {
|
||||
case TEXTURE_2D:
|
||||
glTexImage2D(texture->target, i, internalFormat, width, height, 0, glFormat, GL_UNSIGNED_BYTE, NULL);
|
||||
break;
|
||||
|
||||
case TEXTURE_CUBE:
|
||||
for (int face = 0; face < 6; face++) {
|
||||
for (uint32_t face = 0; face < 6; face++) {
|
||||
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, i, internalFormat, width, height, 0, glFormat, GL_UNSIGNED_BYTE, NULL);
|
||||
}
|
||||
break;
|
||||
|
@ -1306,7 +1309,7 @@ void lovrTextureAllocate(Texture* texture, int width, int height, int depth, Tex
|
|||
}
|
||||
}
|
||||
|
||||
void lovrTextureReplacePixels(Texture* texture, TextureData* textureData, int x, int y, int slice, int mipmap) {
|
||||
void lovrTextureReplacePixels(Texture* texture, TextureData* textureData, uint32_t x, uint32_t y, uint32_t slice, uint32_t mipmap) {
|
||||
lovrGraphicsFlush();
|
||||
lovrAssert(texture->allocated, "Texture is not allocated");
|
||||
|
||||
|
@ -1316,10 +1319,10 @@ void lovrTextureReplacePixels(Texture* texture, TextureData* textureData, int x,
|
|||
}
|
||||
#endif
|
||||
|
||||
int maxWidth = lovrTextureGetWidth(texture, mipmap);
|
||||
int maxHeight = lovrTextureGetHeight(texture, mipmap);
|
||||
int width = textureData->width;
|
||||
int height = textureData->height;
|
||||
uint32_t maxWidth = lovrTextureGetWidth(texture, mipmap);
|
||||
uint32_t maxHeight = lovrTextureGetHeight(texture, mipmap);
|
||||
uint32_t width = textureData->width;
|
||||
uint32_t height = textureData->height;
|
||||
bool overflow = (x + width > maxWidth) || (y + height > maxHeight);
|
||||
lovrAssert(!overflow, "Trying to replace pixels outside the texture's bounds");
|
||||
lovrAssert(mipmap >= 0 && mipmap < texture->mipmapCount, "Invalid mipmap level %d", mipmap);
|
||||
|
@ -1423,7 +1426,7 @@ void lovrTextureSetWrap(Texture* texture, TextureWrap wrap) {
|
|||
|
||||
// Canvas
|
||||
|
||||
Canvas* lovrCanvasInit(Canvas* canvas, int width, int height, CanvasFlags flags) {
|
||||
Canvas* lovrCanvasInit(Canvas* canvas, uint32_t width, uint32_t height, CanvasFlags flags) {
|
||||
canvas->width = width;
|
||||
canvas->height = height;
|
||||
canvas->flags = flags;
|
||||
|
@ -1454,7 +1457,7 @@ Canvas* lovrCanvasInit(Canvas* canvas, int width, int height, CanvasFlags flags)
|
|||
return canvas;
|
||||
}
|
||||
|
||||
Canvas* lovrCanvasInitFromHandle(Canvas* canvas, int width, int height, CanvasFlags flags, uint32_t framebuffer, uint32_t depthBuffer, uint32_t resolveBuffer, int attachmentCount, bool immortal) {
|
||||
Canvas* lovrCanvasInitFromHandle(Canvas* canvas, uint32_t width, uint32_t height, CanvasFlags flags, uint32_t framebuffer, uint32_t depthBuffer, uint32_t resolveBuffer, uint32_t attachmentCount, bool immortal) {
|
||||
canvas->framebuffer = framebuffer;
|
||||
canvas->depthBuffer = depthBuffer;
|
||||
canvas->resolveBuffer = resolveBuffer;
|
||||
|
@ -1474,7 +1477,7 @@ void lovrCanvasDestroy(void* ref) {
|
|||
glDeleteRenderbuffers(1, &canvas->depthBuffer);
|
||||
glDeleteFramebuffers(1, &canvas->resolveBuffer);
|
||||
}
|
||||
for (int i = 0; i < canvas->attachmentCount; i++) {
|
||||
for (uint32_t i = 0; i < canvas->attachmentCount; i++) {
|
||||
lovrRelease(Texture, canvas->attachments[i].texture);
|
||||
}
|
||||
lovrRelease(Texture, canvas->depth.texture);
|
||||
|
@ -1488,8 +1491,8 @@ void lovrCanvasResolve(Canvas* canvas) {
|
|||
lovrGraphicsFlushCanvas(canvas);
|
||||
|
||||
if (canvas->flags.msaa) {
|
||||
int w = canvas->width;
|
||||
int h = canvas->height;
|
||||
uint32_t w = canvas->width;
|
||||
uint32_t h = canvas->height;
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, canvas->framebuffer);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, canvas->resolveBuffer);
|
||||
state.framebuffer = canvas->resolveBuffer;
|
||||
|
@ -1498,7 +1501,7 @@ void lovrCanvasResolve(Canvas* canvas) {
|
|||
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
} else {
|
||||
GLenum buffers[MAX_CANVAS_ATTACHMENTS] = { GL_NONE };
|
||||
for (int i = 0; i < canvas->attachmentCount; i++) {
|
||||
for (uint32_t i = 0; i < canvas->attachmentCount; i++) {
|
||||
buffers[i] = GL_COLOR_ATTACHMENT0 + i;
|
||||
glReadBuffer(i);
|
||||
glDrawBuffers(1, &buffers[i]);
|
||||
|
@ -1510,7 +1513,7 @@ void lovrCanvasResolve(Canvas* canvas) {
|
|||
}
|
||||
|
||||
if (canvas->flags.mipmaps) {
|
||||
for (int i = 0; i < canvas->attachmentCount; i++) {
|
||||
for (uint32_t i = 0; i < canvas->attachmentCount; i++) {
|
||||
Texture* texture = canvas->attachments[i].texture;
|
||||
if (texture->mipmapCount > 1) {
|
||||
lovrGpuBindTexture(texture, 0);
|
||||
|
@ -1522,7 +1525,7 @@ void lovrCanvasResolve(Canvas* canvas) {
|
|||
canvas->needsResolve = false;
|
||||
}
|
||||
|
||||
TextureData* lovrCanvasNewTextureData(Canvas* canvas, int index) {
|
||||
TextureData* lovrCanvasNewTextureData(Canvas* canvas, uint32_t index) {
|
||||
lovrGraphicsFlushCanvas(canvas);
|
||||
lovrGpuBindCanvas(canvas, false);
|
||||
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
#include "graphics/texture.h"
|
||||
|
||||
int lovrTextureGetWidth(Texture* texture, int mipmap) {
|
||||
uint32_t lovrTextureGetWidth(Texture* texture, uint32_t mipmap) {
|
||||
return MAX(texture->width >> mipmap, 1);
|
||||
}
|
||||
|
||||
int lovrTextureGetHeight(Texture* texture, int mipmap) {
|
||||
uint32_t lovrTextureGetHeight(Texture* texture, uint32_t mipmap) {
|
||||
return MAX(texture->height >> mipmap, 1);
|
||||
}
|
||||
|
||||
int lovrTextureGetDepth(Texture* texture, int mipmap) {
|
||||
uint32_t lovrTextureGetDepth(Texture* texture, uint32_t mipmap) {
|
||||
return texture->type == TEXTURE_VOLUME ? MAX(texture->depth >> mipmap, 1) : texture->depth;
|
||||
}
|
||||
|
||||
int lovrTextureGetMipmapCount(Texture* texture) {
|
||||
uint32_t lovrTextureGetMipmapCount(Texture* texture) {
|
||||
return texture->mipmapCount;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,31 +19,31 @@ typedef struct Texture {
|
|||
Ref ref;
|
||||
TextureType type;
|
||||
TextureFormat format;
|
||||
int width;
|
||||
int height;
|
||||
int depth;
|
||||
int mipmapCount;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t depth;
|
||||
uint32_t mipmapCount;
|
||||
TextureFilter filter;
|
||||
TextureWrap wrap;
|
||||
int msaa;
|
||||
uint32_t msaa;
|
||||
bool srgb;
|
||||
bool mipmaps;
|
||||
bool allocated;
|
||||
GPU_TEXTURE_FIELDS
|
||||
} Texture;
|
||||
|
||||
Texture* lovrTextureInit(Texture* texture, TextureType type, struct TextureData** slices, int sliceCount, bool srgb, bool mipmaps, int msaa);
|
||||
Texture* lovrTextureInit(Texture* texture, TextureType type, struct TextureData** slices, uint32_t sliceCount, bool srgb, bool mipmaps, uint32_t msaa);
|
||||
Texture* lovrTextureInitFromHandle(Texture* texture, uint32_t handle, TextureType type);
|
||||
#define lovrTextureCreate(...) lovrTextureInit(lovrAlloc(Texture), __VA_ARGS__)
|
||||
#define lovrTextureCreateFromHandle(...) lovrTextureInitFromHandle(lovrAlloc(Texture), __VA_ARGS__)
|
||||
void lovrTextureDestroy(void* ref);
|
||||
void lovrTextureAllocate(Texture* texture, int width, int height, int depth, TextureFormat format);
|
||||
void lovrTextureReplacePixels(Texture* texture, struct TextureData* data, int x, int y, int slice, int mipmap);
|
||||
int lovrTextureGetWidth(Texture* texture, int mipmap);
|
||||
int lovrTextureGetHeight(Texture* texture, int mipmap);
|
||||
int lovrTextureGetDepth(Texture* texture, int mipmap);
|
||||
int lovrTextureGetMipmapCount(Texture* texture);
|
||||
int lovrTextureGetMSAA(Texture* texture);
|
||||
void lovrTextureAllocate(Texture* texture, uint32_t width, uint32_t height, uint32_t depth, TextureFormat format);
|
||||
void lovrTextureReplacePixels(Texture* texture, struct TextureData* data, uint32_t x, uint32_t y, uint32_t slice, uint32_t mipmap);
|
||||
uint32_t lovrTextureGetWidth(Texture* texture, uint32_t mipmap);
|
||||
uint32_t lovrTextureGetHeight(Texture* texture, uint32_t mipmap);
|
||||
uint32_t lovrTextureGetDepth(Texture* texture, uint32_t mipmap);
|
||||
uint32_t lovrTextureGetMipmapCount(Texture* texture);
|
||||
uint32_t lovrTextureGetMSAA(Texture* texture);
|
||||
TextureType lovrTextureGetType(Texture* texture);
|
||||
TextureFormat lovrTextureGetFormat(Texture* texture);
|
||||
TextureFilter lovrTextureGetFilter(Texture* texture);
|
||||
|
|
Loading…
Reference in New Issue