Explorar o código

GL: image creation

Andre Weissflog %!s(int64=8) %!d(string=hai) anos
pai
achega
767aaffc4d
Modificáronse 3 ficheiros con 568 adicións e 32 borrados
  1. 71 8
      _sokol_gfx.impl.h
  2. 483 16
      _sokol_gfx_gl.impl.h
  3. 14 8
      sokol_gfx.h

+ 71 - 8
_sokol_gfx.impl.h

@@ -72,6 +72,11 @@ void sg_init_image_desc(sg_image_desc* desc) {
     desc->color_format = SG_PIXELFORMAT_RGBA8;
     desc->color_format = SG_PIXELFORMAT_RGBA8;
     desc->depth_format = SG_PIXELFORMAT_NONE;
     desc->depth_format = SG_PIXELFORMAT_NONE;
     desc->sample_count = 1;
     desc->sample_count = 1;
+    desc->min_filter = SG_FILTER_NEAREST;
+    desc->mag_filter = SG_FILTER_NEAREST;
+    desc->wrap_u = SG_WRAP_REPEAT;
+    desc->wrap_v = SG_WRAP_REPEAT;
+    desc->wrap_w = SG_WRAP_REPEAT;
     desc->num_data_items = 0;
     desc->num_data_items = 0;
     desc->data_ptrs = 0;
     desc->data_ptrs = 0;
     desc->data_sizes = 0;
     desc->data_sizes = 0;
@@ -310,6 +315,48 @@ static int _sg_uniform_size(sg_uniform_type type, int count) {
     }
     }
 }
 }
 
 
+/* return true if pixel format is a compressed format */
+static bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) {
+    switch (fmt) {
+        case SG_PIXELFORMAT_DXT1:
+        case SG_PIXELFORMAT_DXT3:
+        case SG_PIXELFORMAT_DXT5:
+        case SG_PIXELFORMAT_PVRTC2_RGB:
+        case SG_PIXELFORMAT_PVRTC4_RGB:
+        case SG_PIXELFORMAT_PVRTC2_RGBA:
+        case SG_PIXELFORMAT_PVRTC4_RGBA:
+        case SG_PIXELFORMAT_ETC2_RGB8:
+        case SG_PIXELFORMAT_ETC2_SRGB8:
+            return true;
+        default:
+            return false;
+    }
+}
+
+/* return true if pixel format is a valid render target format */
+static bool _sg_is_valid_rendertarget_color_format(sg_pixel_format fmt) {
+    switch (fmt) {
+        case SG_PIXELFORMAT_RGBA8:
+        case SG_PIXELFORMAT_R10G10B10A2:
+        case SG_PIXELFORMAT_RGBA32F:
+        case SG_PIXELFORMAT_RGBA16F:
+            return true;
+        default:
+            return false;
+    }
+}
+
+/* return true if pixel format is a valid depth format */
+static bool _sg_is_valid_rendertarget_depth_format(sg_pixel_format fmt) {
+    switch (fmt) {
+        case SG_PIXELFORMAT_DEPTH:
+        case SG_PIXELFORMAT_DEPTHSTENCIL:
+            return true;
+        default:
+            return false;
+    }
+}
+
 /*-- resource pool slots (must be defined before rendering backend) ----------*/
 /*-- resource pool slots (must be defined before rendering backend) ----------*/
 typedef struct {
 typedef struct {
     sg_id id;
     sg_id id;
@@ -658,8 +705,15 @@ static void _sg_validate_image_desc(const sg_image_desc* desc) {
     #if SOKOL_DEBUG
     #if SOKOL_DEBUG
     if (desc->render_target) {
     if (desc->render_target) {
         SOKOL_ASSERT(desc->usage == SG_USAGE_IMMUTABLE);
         SOKOL_ASSERT(desc->usage == SG_USAGE_IMMUTABLE);
+        SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(desc->color_format));
+        if (desc->depth_format != SG_PIXELFORMAT_NONE) {
+            SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(desc->depth_format));
+        }
         SOKOL_ASSERT((0 == desc->num_data_items) && (0 == desc->data_ptrs) && (0 == desc->data_sizes));
         SOKOL_ASSERT((0 == desc->num_data_items) && (0 == desc->data_ptrs) && (0 == desc->data_sizes));
     }
     }
+    else {
+        SOKOL_ASSERT(desc->depth_format == SG_PIXELFORMAT_NONE);
+    }
     if (desc->num_data_items > 0) {
     if (desc->num_data_items > 0) {
         SOKOL_ASSERT(desc->data_ptrs);
         SOKOL_ASSERT(desc->data_ptrs);
         SOKOL_ASSERT(desc->data_sizes);
         SOKOL_ASSERT(desc->data_sizes);
@@ -811,7 +865,7 @@ void sg_init_buffer(sg_id buf_id, const sg_buffer_desc* desc) {
     _sg_validate_buffer_desc(desc);
     _sg_validate_buffer_desc(desc);
     _sg_buffer* buf = _sg_lookup_buffer(&_sg->pools, buf_id);
     _sg_buffer* buf = _sg_lookup_buffer(&_sg->pools, buf_id);
     SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC);
     SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC);
-    _sg_create_buffer(buf, desc);
+    _sg_create_buffer(&_sg->backend, buf, desc);
     SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID)||(buf->slot.state == SG_RESOURCESTATE_FAILED));
     SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID)||(buf->slot.state == SG_RESOURCESTATE_FAILED));
 }
 }
 
 
@@ -821,7 +875,7 @@ void sg_init_image(sg_id img_id, const sg_image_desc* desc) {
     _sg_validate_image_desc(desc);
     _sg_validate_image_desc(desc);
     _sg_image* img = _sg_lookup_image(&_sg->pools, img_id);
     _sg_image* img = _sg_lookup_image(&_sg->pools, img_id);
     SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC);
     SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC);
-    _sg_create_image(img, desc);
+    _sg_create_image(&_sg->backend, img, desc);
     SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID)||(img->slot.state == SG_RESOURCESTATE_FAILED));
     SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID)||(img->slot.state == SG_RESOURCESTATE_FAILED));
 }
 }
 
 
@@ -831,7 +885,7 @@ void sg_init_shader(sg_id shd_id, const sg_shader_desc* desc) {
     _sg_validate_shader_desc(desc);
     _sg_validate_shader_desc(desc);
     _sg_shader* shd = _sg_lookup_shader(&_sg->pools, shd_id);
     _sg_shader* shd = _sg_lookup_shader(&_sg->pools, shd_id);
     SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC);
     SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC);
-    _sg_create_shader(shd, desc);
+    _sg_create_shader(&_sg->backend, shd, desc);
     SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID)||(shd->slot.state == SG_RESOURCESTATE_FAILED));
     SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID)||(shd->slot.state == SG_RESOURCESTATE_FAILED));
 }
 }
 
 
@@ -843,7 +897,7 @@ void sg_init_pipeline(sg_id pip_id, const sg_pipeline_desc* desc) {
     SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC);
     SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC);
     _sg_shader* shd = _sg_lookup_shader(&_sg->pools, desc->shader);
     _sg_shader* shd = _sg_lookup_shader(&_sg->pools, desc->shader);
     SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_VALID);
     SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_VALID);
-    _sg_create_pipeline(pip, shd, desc);
+    _sg_create_pipeline(&_sg->backend, pip, shd, desc);
     SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID)||(pip->slot.state == SG_RESOURCESTATE_FAILED)); 
     SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID)||(pip->slot.state == SG_RESOURCESTATE_FAILED)); 
 }
 }
 
 
@@ -853,7 +907,7 @@ void sg_init_pass(sg_id pass_id, const sg_pass_desc* desc) {
     _sg_validate_pass_desc(desc);
     _sg_validate_pass_desc(desc);
     _sg_pass* pass = _sg_lookup_pass(&_sg->pools, pass_id);
     _sg_pass* pass = _sg_lookup_pass(&_sg->pools, pass_id);
     SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC);
     SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC);
-    _sg_create_pass(pass, desc);
+    _sg_create_pass(&_sg->backend, pass, desc);
     SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID)||(pass->slot.state == SG_RESOURCESTATE_FAILED)); 
     SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID)||(pass->slot.state == SG_RESOURCESTATE_FAILED)); 
 }
 }
 
 
@@ -908,16 +962,25 @@ void sg_destroy_buffer(sg_id buf_id) {
     SOKOL_ASSERT(_sg);
     SOKOL_ASSERT(_sg);
     _sg_buffer* buf = _sg_lookup_buffer(&_sg->pools, buf_id);
     _sg_buffer* buf = _sg_lookup_buffer(&_sg->pools, buf_id);
     if (buf) {
     if (buf) {
-        _sg_destroy_buffer(buf);
+        _sg_destroy_buffer(&_sg->backend, buf);
         _sg_pool_free_id(&_sg->pools.pool[SG_RESOURCETYPE_BUFFER], buf_id);
         _sg_pool_free_id(&_sg->pools.pool[SG_RESOURCETYPE_BUFFER], buf_id);
     }
     }
 }
 }
 
 
+void sg_destroy_image(sg_id img_id) {
+    SOKOL_ASSERT(_sg);
+    _sg_image* img = _sg_lookup_image(&_sg->pools, img_id);
+    if (img) {
+        _sg_destroy_image(&_sg->backend, img);
+        _sg_pool_free_id(&_sg->pools.pool[SG_RESOURCETYPE_IMAGE], img_id);
+    }
+}
+
 void sg_destroy_shader(sg_id shd_id) {
 void sg_destroy_shader(sg_id shd_id) {
     SOKOL_ASSERT(_sg);
     SOKOL_ASSERT(_sg);
     _sg_shader* shd = _sg_lookup_shader(&_sg->pools, shd_id);
     _sg_shader* shd = _sg_lookup_shader(&_sg->pools, shd_id);
     if (shd) {
     if (shd) {
-        _sg_destroy_shader(shd);
+        _sg_destroy_shader(&_sg->backend, shd);
         _sg_pool_free_id(&_sg->pools.pool[SG_RESOURCETYPE_SHADER], shd_id);
         _sg_pool_free_id(&_sg->pools.pool[SG_RESOURCETYPE_SHADER], shd_id);
     }
     }
 }
 }
@@ -926,7 +989,7 @@ void sg_destroy_pipeline(sg_id pip_id) {
     SOKOL_ASSERT(_sg);
     SOKOL_ASSERT(_sg);
     _sg_pipeline* pip = _sg_lookup_pipeline(&_sg->pools, pip_id);
     _sg_pipeline* pip = _sg_lookup_pipeline(&_sg->pools, pip_id);
     if (pip) {
     if (pip) {
-        _sg_destroy_pipeline(pip);
+        _sg_destroy_pipeline(&_sg->backend, pip);
         _sg_pool_free_id(&_sg->pools.pool[SG_RESOURCETYPE_PIPELINE], pip_id);
         _sg_pool_free_id(&_sg->pools.pool[SG_RESOURCETYPE_PIPELINE], pip_id);
     }
     }
 }
 }

+ 483 - 16
_sokol_gfx_gl.impl.h

@@ -12,6 +12,33 @@ enum {
 #ifndef GL_UNSIGNED_INT_2_10_10_10_REV
 #ifndef GL_UNSIGNED_INT_2_10_10_10_REV
 #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
 #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
 #endif
 #endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
+#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
+#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
+#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
+#endif
+#ifndef GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG
+#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
+#endif
+#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG
+#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
+#endif
+#ifndef GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
+#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
+#endif
+#ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG
+#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
+#endif
+#ifndef GL_COMPRESSED_RGB8_ETC2
+#define GL_COMPRESSED_RGB8_ETC2 0x9274
+#endif
+#ifndef GL_COMPRESSED_SRGB8_ETC2
+#define GL_COMPRESSED_SRGB8_ETC2 0x9275
+#endif
 
 
 #define _SG_GL_CHECK_ERROR() { SOKOL_ASSERT(glGetError() == GL_NO_ERROR); } 
 #define _SG_GL_CHECK_ERROR() { SOKOL_ASSERT(glGetError() == GL_NO_ERROR); } 
 
 
@@ -23,6 +50,18 @@ static GLenum _sg_gl_buffer_target(sg_buffer_type t) {
     }
     }
 }
 }
 
 
+static GLenum _sg_gl_texture_target(sg_image_type t) {
+    switch (t) {
+        case SG_IMAGETYPE_2D:   return GL_TEXTURE_2D;
+        case SG_IMAGETYPE_CUBE: return GL_TEXTURE_CUBE_MAP;
+        #if !defined(SOKOL_USE_GLES2)
+        case SG_IMAGETYPE_3D:       return GL_TEXTURE_3D;
+        case SG_IMAGETYPE_ARRAY:    return GL_TEXTURE_2D_ARRAY;
+        #endif
+        default: return 0;
+    }
+}
+
 static GLenum _sg_gl_usage(sg_usage u) {
 static GLenum _sg_gl_usage(sg_usage u) {
     switch (u) {
     switch (u) {
         case SG_USAGE_IMMUTABLE:    return GL_STATIC_DRAW;
         case SG_USAGE_IMMUTABLE:    return GL_STATIC_DRAW;
@@ -175,6 +214,184 @@ static GLenum _sg_gl_cull_face(sg_face f) {
     }
     }
 }
 }
 
 
+static GLenum _sg_gl_filter(sg_filter f) {
+    switch (f) {
+        case SG_FILTER_NEAREST:                 return GL_NEAREST;
+        case SG_FILTER_LINEAR:                  return GL_LINEAR;
+        case SG_FILTER_NEAREST_MIPMAP_NEAREST:  return GL_NEAREST_MIPMAP_NEAREST;
+        case SG_FILTER_NEAREST_MIPMAP_LINEAR:   return GL_NEAREST_MIPMAP_LINEAR;
+        case SG_FILTER_LINEAR_MIPMAP_NEAREST:   return GL_LINEAR_MIPMAP_NEAREST;
+        case SG_FILTER_LINEAR_MIPMAP_LINEAR:    return GL_LINEAR_MIPMAP_LINEAR;
+    }
+}
+
+static GLenum _sg_gl_wrap(sg_wrap w) {
+    switch (w) {
+        case SG_WRAP_CLAMP_TO_EDGE:     return GL_CLAMP_TO_EDGE;
+        case SG_WRAP_REPEAT:            return GL_REPEAT;
+        case SG_WRAP_MIRRORED_REPEAT:   return GL_MIRRORED_REPEAT;
+    }
+}
+
+static GLenum _sg_gl_teximage_type(sg_pixel_format fmt) {
+    switch (fmt) {
+        case SG_PIXELFORMAT_RGBA32F:
+        case SG_PIXELFORMAT_R32F:
+            return GL_FLOAT;
+        case SG_PIXELFORMAT_RGBA16F:
+        case SG_PIXELFORMAT_R16F:
+            return GL_HALF_FLOAT;
+        case SG_PIXELFORMAT_RGBA8:
+        case SG_PIXELFORMAT_RGB8:
+        case SG_PIXELFORMAT_L8:
+            return GL_UNSIGNED_BYTE;
+        case SG_PIXELFORMAT_R10G10B10A2:
+            return GL_UNSIGNED_INT_2_10_10_10_REV;
+        case SG_PIXELFORMAT_R5G5B5A1:
+            return GL_UNSIGNED_SHORT_5_5_5_1;
+        case SG_PIXELFORMAT_R5G6B5:
+            return GL_UNSIGNED_SHORT_5_6_5;
+        case SG_PIXELFORMAT_RGBA4:
+            return GL_UNSIGNED_SHORT_4_4_4_4;
+        case SG_PIXELFORMAT_DEPTH:
+            /* FIXME */
+            return GL_UNSIGNED_SHORT;
+        case SG_PIXELFORMAT_DEPTHSTENCIL:
+            /* FIXME */
+            return GL_UNSIGNED_INT_24_8;
+        default:
+            SOKOL_LOG("_sg_gl_teximage_type(): invalid pixel format!\n");
+            return 0;
+    }
+}
+
+static GLenum _sg_gl_teximage_format(sg_pixel_format fmt) {
+    switch (fmt) {
+        case SG_PIXELFORMAT_NONE:
+            return 0;
+        case SG_PIXELFORMAT_RGBA8:
+        case SG_PIXELFORMAT_R5G5B5A1:
+        case SG_PIXELFORMAT_RGBA4:
+        case SG_PIXELFORMAT_RGBA32F:
+        case SG_PIXELFORMAT_RGBA16F:
+        case SG_PIXELFORMAT_R10G10B10A2:
+            return GL_RGBA;
+        case SG_PIXELFORMAT_RGB8:
+        case SG_PIXELFORMAT_R5G6B5:
+            return GL_RGB;
+        case SG_PIXELFORMAT_L8:
+        case SG_PIXELFORMAT_R32F:
+        case SG_PIXELFORMAT_R16F:
+            #if defined(SOKOL_USE_GLES2)
+            return GL_LUMINANCE;
+            #else
+            return GL_RED;
+            #endif
+        case SG_PIXELFORMAT_DEPTH:
+            return GL_DEPTH_COMPONENT;
+        case SG_PIXELFORMAT_DEPTHSTENCIL:
+            return GL_DEPTH_STENCIL;
+        case SG_PIXELFORMAT_DXT1:
+            return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
+        case SG_PIXELFORMAT_DXT3:
+            return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+        case SG_PIXELFORMAT_DXT5:
+            return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+        case SG_PIXELFORMAT_PVRTC2_RGB:
+            return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
+        case SG_PIXELFORMAT_PVRTC4_RGB:
+            return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; 
+        case SG_PIXELFORMAT_PVRTC2_RGBA:
+            return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
+        case SG_PIXELFORMAT_PVRTC4_RGBA:
+            return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
+        case SG_PIXELFORMAT_ETC2_RGB8:
+            return GL_COMPRESSED_RGB8_ETC2;
+        case SG_PIXELFORMAT_ETC2_SRGB8:
+            return GL_COMPRESSED_SRGB8_ETC2;
+    }
+}
+
+static GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) {
+    #if defined(SOKOL_USE_GLES2)
+    return _sg_gl_teximage_format(fmt);
+    #else
+    switch (fmt) {
+        case SG_PIXELFORMAT_NONE:
+            return 0;
+        case SG_PIXELFORMAT_RGBA8:
+            return GL_RGBA8;
+        case SG_PIXELFORMAT_RGB8:
+            return GL_RGB8;
+        case SG_PIXELFORMAT_RGBA4:
+            return GL_RGBA4;
+        case SG_PIXELFORMAT_R5G6B5:
+            #if defined(SOKOL_USE_GLES3)
+                return GL_RGB565;
+            #else
+                return GL_RGB5;
+            #endif
+        case SG_PIXELFORMAT_R5G5B5A1:
+            return GL_RGB5_A1;
+        case SG_PIXELFORMAT_R10G10B10A2:
+            return GL_RGB10_A2;
+        case SG_PIXELFORMAT_RGBA32F:
+            return GL_RGBA32F;
+        case SG_PIXELFORMAT_RGBA16F:
+            return GL_RGBA16F;
+        case SG_PIXELFORMAT_R32F:
+            return GL_R32F;
+        case SG_PIXELFORMAT_R16F:
+            return GL_R16F;
+        case SG_PIXELFORMAT_L8:
+            return GL_R8;
+        case SG_PIXELFORMAT_DEPTH:
+            /* FIXME */
+            return GL_DEPTH_COMPONENT16;
+        case SG_PIXELFORMAT_DEPTHSTENCIL:
+            /* FIXME */
+            return GL_DEPTH24_STENCIL8;
+        case SG_PIXELFORMAT_DXT1:
+            return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
+        case SG_PIXELFORMAT_DXT3:
+            return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+        case SG_PIXELFORMAT_DXT5:
+            return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+        case SG_PIXELFORMAT_PVRTC2_RGB:
+            return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
+        case SG_PIXELFORMAT_PVRTC4_RGB:
+            return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; 
+        case SG_PIXELFORMAT_PVRTC2_RGBA:
+            return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
+        case SG_PIXELFORMAT_PVRTC4_RGBA:
+            return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
+        case SG_PIXELFORMAT_ETC2_RGB8:
+            return GL_COMPRESSED_RGB8_ETC2;
+        case SG_PIXELFORMAT_ETC2_SRGB8:
+            return GL_COMPRESSED_SRGB8_ETC2;
+    }
+    #endif
+}
+
+static GLenum _sg_gl_cubeface_target(int face_index) {
+    switch (face_index) {
+        case 0: return GL_TEXTURE_CUBE_MAP_POSITIVE_X;
+        case 1: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X;
+        case 2: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y;
+        case 3: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y;
+        case 4: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z;
+        default: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
+    }
+}
+
+static GLenum _sg_gl_depth_attachment_format(sg_pixel_format fmt) {
+    switch (fmt) {
+        case SG_PIXELFORMAT_DEPTH:          return GL_DEPTH_COMPONENT16; /* FIXME */
+        case SG_PIXELFORMAT_DEPTHSTENCIL:   return GL_DEPTH24_STENCIL8;  /* FIXME */
+        default:    return 0;
+    }
+}
+
 /*-- GL backend resource declarations ----------------------------------------*/
 /*-- GL backend resource declarations ----------------------------------------*/
 typedef struct {
 typedef struct {
     _sg_slot slot;
     _sg_slot slot;
@@ -203,11 +420,57 @@ static void _sg_init_buffer(_sg_buffer* buf) {
 
 
 typedef struct {
 typedef struct {
     _sg_slot slot;
     _sg_slot slot;
+    sg_image_type type;
+    bool render_target;
+    uint16_t width;
+    uint16_t height;
+    uint16_t depth;
+    uint16_t num_mipmaps;
+    sg_usage usage;
+    sg_pixel_format color_format;
+    sg_pixel_format depth_format;
+    int sample_count;
+    sg_filter min_filter;
+    sg_filter mag_filter;
+    sg_wrap wrap_u;
+    sg_wrap wrap_v;
+    sg_wrap wrap_w;
+    GLenum gl_target;
+    GLuint gl_depth_render_buffer;
+    GLuint gl_msaa_render_buffer;
+    uint32_t upd_frame_index;
+    int num_slots;
+    int active_slot;
+    GLuint gl_tex[_SG_GL_NUM_UPDATE_SLOTS];
 } _sg_image;
 } _sg_image;
 
 
 static void _sg_init_image(_sg_image* img) {
 static void _sg_init_image(_sg_image* img) {
     SOKOL_ASSERT(img);
     SOKOL_ASSERT(img);
     _sg_init_slot(&img->slot);
     _sg_init_slot(&img->slot);
+    img->type = SG_IMAGETYPE_INVALID;
+    img->render_target = false;
+    img->width = 0;
+    img->height = 0;
+    img->depth = 0;
+    img->num_mipmaps = 0;
+    img->usage = SG_USAGE_IMMUTABLE;
+    img->color_format = SG_PIXELFORMAT_NONE;
+    img->depth_format = SG_PIXELFORMAT_NONE;
+    img->sample_count = 0;
+    img->min_filter = SG_FILTER_NEAREST;
+    img->mag_filter = SG_FILTER_NEAREST;
+    img->wrap_u = SG_WRAP_REPEAT;
+    img->wrap_v = SG_WRAP_REPEAT;
+    img->wrap_w = SG_WRAP_REPEAT;
+    img->gl_target = 0;
+    img->gl_depth_render_buffer = 0;
+    img->gl_msaa_render_buffer = 0;
+    img->upd_frame_index = 0;
+    img->num_slots = 0;
+    img->active_slot = 0;
+    for (int i = 0; i < _SG_GL_NUM_UPDATE_SLOTS; i++) {
+        img->gl_tex[i] = 0;
+    }
 }
 }
 
 
 typedef struct {
 typedef struct {
@@ -447,7 +710,7 @@ static bool _sg_query_feature(_sg_backend* state, sg_feature f) {
 }
 }
 
 
 /*-- GL backend resource creation and destruction ----------------------------*/
 /*-- GL backend resource creation and destruction ----------------------------*/
-static void _sg_create_buffer(_sg_buffer* buf, const sg_buffer_desc* desc) {
+static void _sg_create_buffer(_sg_backend* state, _sg_buffer* buf, const sg_buffer_desc* desc) {
     SOKOL_ASSERT(buf && desc);
     SOKOL_ASSERT(buf && desc);
     SOKOL_ASSERT(buf->slot.state == SG_RESOURCESTATE_ALLOC);
     SOKOL_ASSERT(buf->slot.state == SG_RESOURCESTATE_ALLOC);
     SOKOL_ASSERT(desc->data_size <= desc->size);
     SOKOL_ASSERT(desc->data_size <= desc->size);
@@ -455,12 +718,7 @@ static void _sg_create_buffer(_sg_buffer* buf, const sg_buffer_desc* desc) {
     buf->size = desc->size;
     buf->size = desc->size;
     buf->type = desc->type;
     buf->type = desc->type;
     buf->usage = desc->usage;
     buf->usage = desc->usage;
-    if (desc->usage == SG_USAGE_IMMUTABLE) {
-        buf->num_slots = 1;
-    }
-    else {
-        buf->num_slots = _SG_GL_NUM_UPDATE_SLOTS;
-    }
+    buf->num_slots = desc->usage == SG_USAGE_STREAM ? _SG_GL_NUM_UPDATE_SLOTS : 1;
     buf->active_slot = 0;
     buf->active_slot = 0;
     GLenum gl_target = _sg_gl_buffer_target(buf->type);
     GLenum gl_target = _sg_gl_buffer_target(buf->type);
     GLenum gl_usage  = _sg_gl_usage(buf->usage);
     GLenum gl_usage  = _sg_gl_usage(buf->usage);
@@ -478,20 +736,224 @@ static void _sg_create_buffer(_sg_buffer* buf, const sg_buffer_desc* desc) {
     buf->slot.state = SG_RESOURCESTATE_VALID;
     buf->slot.state = SG_RESOURCESTATE_VALID;
 }
 }
 
 
-static void _sg_destroy_buffer(_sg_buffer* buf) {
+static void _sg_destroy_buffer(_sg_backend* state, _sg_buffer* buf) {
     SOKOL_ASSERT(buf);
     SOKOL_ASSERT(buf);
+    _SG_GL_CHECK_ERROR();
     for (int slot = 0; slot < buf->num_slots; slot++) {
     for (int slot = 0; slot < buf->num_slots; slot++) {
         if (buf->gl_buf[slot]) {
         if (buf->gl_buf[slot]) {
             glDeleteBuffers(1, &buf->gl_buf[slot]);
             glDeleteBuffers(1, &buf->gl_buf[slot]);
         }
         }
     }
     }
+    _SG_GL_CHECK_ERROR();
     _sg_init_buffer(buf);
     _sg_init_buffer(buf);
 }
 }
 
 
-static void _sg_create_image(_sg_image* img, const sg_image_desc* desc) {
+static bool _sg_gl_valid_texture_format(_sg_backend* state, sg_pixel_format fmt) {
+    SOKOL_ASSERT(state);
+    switch (fmt) {
+        case SG_PIXELFORMAT_DXT1:
+        case SG_PIXELFORMAT_DXT3:
+        case SG_PIXELFORMAT_DXT5:
+            return state->features[SG_FEATURE_TEXTURE_COMPRESSION_DXT];
+        case SG_PIXELFORMAT_PVRTC2_RGB:
+        case SG_PIXELFORMAT_PVRTC4_RGB:
+        case SG_PIXELFORMAT_PVRTC2_RGBA:
+        case SG_PIXELFORMAT_PVRTC4_RGBA:
+            return state->features[SG_FEATURE_TEXTURE_COMPRESSION_PVRTC];
+        case SG_PIXELFORMAT_ETC2_RGB8:
+        case SG_PIXELFORMAT_ETC2_SRGB8:
+            return state->features[SG_FEATURE_TEXTURE_COMPRESSION_ETC2];
+        default:
+            return true;
+    }
+}
+
+static void _sg_create_image(_sg_backend* state, _sg_image* img, const sg_image_desc* desc) {
+    SOKOL_ASSERT(state);
     SOKOL_ASSERT(img && desc);
     SOKOL_ASSERT(img && desc);
-    /* FIXME */
-    img->slot.state = SG_RESOURCESTATE_FAILED;
+    SOKOL_ASSERT(img->slot.state == SG_RESOURCESTATE_ALLOC);
+    _SG_GL_CHECK_ERROR();
+    img->type = desc->type;
+    img->render_target = desc->render_target;
+    img->width = desc->width;
+    img->height = desc->height;
+    img->depth = desc->depth;
+    img->num_mipmaps = desc->num_mipmaps;
+    img->usage = desc->usage;
+    img->color_format = desc->color_format;
+    img->depth_format = desc->depth_format;
+    img->sample_count = desc->sample_count;
+    img->min_filter = desc->min_filter;
+    img->mag_filter = desc->mag_filter;
+    img->wrap_u = desc->wrap_u;
+    img->wrap_v = desc->wrap_v;
+    img->wrap_w = desc->wrap_w;
+
+    /* check if texture format is support */
+    if (!_sg_gl_valid_texture_format(state, img->color_format)) {
+        SOKOL_LOG("compressed texture format not supported by GL context\n");
+        img->slot.state = SG_RESOURCESTATE_FAILED;
+        return;
+    }
+    /* check for optional texture types */
+    if ((img->type == SG_IMAGETYPE_3D) && !state->features[SG_FEATURE_TEXTURE_3D]) {
+        SOKOL_LOG("3D textures not supported by GL context\n");
+        img->slot.state = SG_RESOURCESTATE_FAILED;
+        return;
+    }
+    if ((img->type == SG_IMAGETYPE_ARRAY) && !state->features[SG_FEATURE_TEXTURE_ARRAY]) {
+        SOKOL_LOG("array textures not supported by GL context\n");
+        img->slot.state = SG_RESOURCESTATE_FAILED;
+        return;
+    }
+    if ((img->depth_format != SG_PIXELFORMAT_NONE) && !_sg_is_valid_rendertarget_depth_format(img->depth_format)) {
+        SOKOL_LOG("depth_format is not a valid render target depth format!\n");
+        img->slot.state = SG_RESOURCESTATE_FAILED;
+        return;
+    }
+
+    /* create 1 or 2 GL textures, depending on requested update strategy */
+    img->num_slots = img->usage == SG_USAGE_STREAM ? _SG_GL_NUM_UPDATE_SLOTS : 1;
+    img->active_slot = 0;
+
+    /* create the GL color texture(s) */
+    const GLenum gl_internal_format = _sg_gl_teximage_internal_format(img->color_format);
+    const GLenum gl_target = _sg_gl_texture_target(img->type);
+    const GLenum gl_format = _sg_gl_teximage_format(img->color_format);
+    const bool is_compressed = _sg_is_compressed_pixel_format(img->color_format);
+    for (int slot = 0; slot < img->num_slots; slot++) {
+        glGenTextures(1, &img->gl_tex[slot]);
+        glActiveTexture(GL_TEXTURE0);
+        glBindTexture(gl_target, img->gl_tex[slot]);
+        GLenum gl_min_filter = _sg_gl_filter(desc->min_filter);
+        GLenum gl_mag_filter = _sg_gl_filter(desc->mag_filter);
+        if (1 == img->num_mipmaps) {
+            if ((gl_min_filter==GL_NEAREST_MIPMAP_NEAREST)||(gl_min_filter==GL_NEAREST_MIPMAP_LINEAR)) {
+                gl_min_filter = GL_NEAREST;
+            }
+            else if ((gl_min_filter==GL_LINEAR_MIPMAP_NEAREST)||(gl_min_filter==GL_LINEAR_MIPMAP_LINEAR)) {
+                gl_min_filter = GL_LINEAR;
+            }
+            glTexParameteri(gl_target, GL_TEXTURE_MIN_FILTER, gl_min_filter);
+            glTexParameteri(gl_target, GL_TEXTURE_MAG_FILTER, gl_mag_filter);
+            if (img->type == SG_IMAGETYPE_CUBE) {
+                glTexParameteri(gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+                glTexParameteri(gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+            }
+            else {
+                glTexParameteri(gl_target, GL_TEXTURE_WRAP_S, _sg_gl_wrap(img->wrap_u));
+                glTexParameteri(gl_target, GL_TEXTURE_WRAP_T, _sg_gl_wrap(img->wrap_v));
+                #if !defined(SOKOL_USE_GLES2)
+                if (img->type == SG_IMAGETYPE_3D) {
+                    glTexParameteri(gl_target, GL_TEXTURE_WRAP_R, _sg_gl_wrap(img->wrap_w));
+                }
+                #endif
+            }
+        }
+        const uint16_t num_faces = img->type == SG_IMAGETYPE_CUBE ? 6 : 1;
+        int data_index = 0;
+        for (uint16_t mip_index = 0; mip_index < img->num_mipmaps; mip_index++) {
+            for (uint16_t face_index = 0; face_index < num_faces; face_index++, data_index++) {
+                GLenum gl_img_target = gl_target;
+                if (SG_IMAGETYPE_CUBE == img->type) {
+                    gl_img_target = _sg_gl_cubeface_target(face_index);
+                }
+                const GLvoid* data_ptr = 0;
+                int data_size = 0;
+                if (data_index < desc->num_data_items) {
+                    SOKOL_ASSERT(desc->data_ptrs && desc->data_ptrs[data_index]);
+                    SOKOL_ASSERT(desc->data_sizes && (desc->data_sizes[data_index] > 0));
+                    data_ptr = desc->data_ptrs[data_index];
+                    data_size = desc->data_sizes[data_index];
+                }
+                uint16_t mip_width = img->width >> mip_index;
+                if (mip_width == 0) {
+                    mip_width = 1;
+                }
+                uint16_t mip_height = img->height >> mip_index;
+                if (mip_height == 0) {
+                    mip_height = 1;
+                }
+                if ((SG_IMAGETYPE_2D == img->type) || (SG_IMAGETYPE_CUBE == img->type)) {
+                    if (is_compressed) {
+                        glCompressedTexImage2D(gl_img_target, mip_index, gl_internal_format,
+                            mip_width, mip_height, 0, data_size, data_ptr);
+                    }
+                    else {
+                        const GLenum gl_type = _sg_gl_teximage_type(img->color_format);
+                        glTexImage2D(gl_img_target, mip_index, gl_internal_format,
+                            mip_width, mip_height, 0, gl_format, gl_type, data_ptr);
+                    }
+                }
+                #if !defined(SOKOL_USE_GLES2)
+                else if ((SG_IMAGETYPE_3D == img->type) || (SG_IMAGETYPE_ARRAY == img->type)) {
+                    uint16_t mip_depth = img->depth >> mip_index;
+                    if (mip_depth == 0) {
+                        mip_depth = 1;
+                    }
+                    if (is_compressed) {
+                        glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format,
+                            mip_width, mip_height, mip_depth, 0, data_size, data_ptr);
+                    }
+                    else {
+                        const GLenum gl_type = _sg_gl_teximage_type(img->color_format);
+                        glTexImage3D(gl_img_target, mip_index, gl_internal_format,
+                            mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr);
+                    }
+                }
+                #endif
+            }
+        }
+    }
+
+    /* additional render target stuff */
+    if (img->render_target) {
+        /* MSAA render buffer */
+        #if !defined(SOKOL_USE_GLES2)
+        const bool msaa = (img->sample_count > 1) && (state->features[SG_FEATURE_MSAA_RENDER_TARGETS]);
+        if (msaa) {
+            glGenRenderbuffers(1, &img->gl_msaa_render_buffer);
+            glBindRenderbuffer(GL_RENDERBUFFER, img->gl_msaa_render_buffer);
+            glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->sample_count, gl_internal_format, img->width, img->height);
+        }
+        #endif
+
+        /* depth buffer */
+        if (img->depth_format != SG_PIXELFORMAT_NONE) {
+            glGenRenderbuffers(1, &img->gl_depth_render_buffer);
+            glBindRenderbuffer(GL_RENDERBUFFER, img->gl_depth_render_buffer);
+            GLenum gl_depth_format = _sg_gl_depth_attachment_format(img->depth_format);
+            #if !defined(SOKOL_US_GLES2)
+            if (msaa) {
+                glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->sample_count, gl_depth_format, img->width, img->height);
+            }
+            else
+            #endif
+            {
+                glRenderbufferStorage(GL_RENDERBUFFER, gl_depth_format, img->width, img->height);
+            }
+        }
+    }
+    _SG_GL_CHECK_ERROR();
+    img->slot.state = SG_RESOURCESTATE_VALID;
+}
+
+static void _sg_destroy_image(_sg_backend* state, _sg_image* img) {
+    SOKOL_ASSERT(img);
+    _SG_GL_CHECK_ERROR();
+    for (int slot = 0; slot < img->num_slots; slot++) {
+        if (img->gl_tex[slot]) {
+            glDeleteTextures(1, &img->gl_tex[slot]);
+        }
+    }
+    if (img->gl_depth_render_buffer) {
+        glDeleteRenderbuffers(1, &img->gl_depth_render_buffer);
+    }
+    if (img->gl_msaa_render_buffer) {
+        glDeleteRenderbuffers(1, &img->gl_msaa_render_buffer);
+    }
+    _SG_GL_CHECK_ERROR();
+    _sg_init_image(img);
 }
 }
 
 
 static GLuint _sg_compile_shader(sg_shader_stage stage, const char* src) {
 static GLuint _sg_compile_shader(sg_shader_stage stage, const char* src) {
@@ -519,8 +981,9 @@ static GLuint _sg_compile_shader(sg_shader_stage stage, const char* src) {
     return gl_shd;
     return gl_shd;
 }
 }
 
 
-static void _sg_create_shader(_sg_shader* shd, const sg_shader_desc* desc) {
+static void _sg_create_shader(_sg_backend* state, _sg_shader* shd, const sg_shader_desc* desc) {
     SOKOL_ASSERT(shd && desc);
     SOKOL_ASSERT(shd && desc);
+    SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_ALLOC);
     SOKOL_ASSERT(!shd->gl_prog);
     SOKOL_ASSERT(!shd->gl_prog);
     _SG_GL_CHECK_ERROR();
     _SG_GL_CHECK_ERROR();
     GLuint gl_vs = _sg_compile_shader(SG_SHADERSTAGE_VS, desc->vs.source);
     GLuint gl_vs = _sg_compile_shader(SG_SHADERSTAGE_VS, desc->vs.source);
@@ -591,16 +1054,19 @@ static void _sg_create_shader(_sg_shader* shd, const sg_shader_desc* desc) {
     shd->slot.state = SG_RESOURCESTATE_VALID;
     shd->slot.state = SG_RESOURCESTATE_VALID;
 }
 }
 
 
-static void _sg_destroy_shader(_sg_shader* shd) {
+static void _sg_destroy_shader(_sg_backend* state, _sg_shader* shd) {
     SOKOL_ASSERT(shd);
     SOKOL_ASSERT(shd);
+    _SG_GL_CHECK_ERROR();
     if (shd->gl_prog) {
     if (shd->gl_prog) {
         glDeleteShader(shd->gl_prog);
         glDeleteShader(shd->gl_prog);
     }
     }
+    _SG_GL_CHECK_ERROR();
     _sg_init_shader(shd);
     _sg_init_shader(shd);
 }
 }
 
 
-static void _sg_create_pipeline(_sg_pipeline* pip, _sg_shader* shd, const sg_pipeline_desc* desc) {
+static void _sg_create_pipeline(_sg_backend* state, _sg_pipeline* pip, _sg_shader* shd, const sg_pipeline_desc* desc) {
     SOKOL_ASSERT(pip && desc);
     SOKOL_ASSERT(pip && desc);
+    SOKOL_ASSERT(pip->slot.state == SG_RESOURCESTATE_ALLOC);
     SOKOL_ASSERT(!pip->shader && pip->shader_id == SG_INVALID_ID);
     SOKOL_ASSERT(!pip->shader && pip->shader_id == SG_INVALID_ID);
     SOKOL_ASSERT(desc->shader == shd->slot.id);
     SOKOL_ASSERT(desc->shader == shd->slot.id);
     SOKOL_ASSERT(shd->gl_prog);
     SOKOL_ASSERT(shd->gl_prog);
@@ -656,13 +1122,14 @@ static void _sg_create_pipeline(_sg_pipeline* pip, _sg_shader* shd, const sg_pip
     pip->slot.state = SG_RESOURCESTATE_VALID;
     pip->slot.state = SG_RESOURCESTATE_VALID;
 }
 }
 
 
-static void _sg_destroy_pipeline(_sg_pipeline* pip) {
+static void _sg_destroy_pipeline(_sg_backend* state, _sg_pipeline* pip) {
     SOKOL_ASSERT(pip);
     SOKOL_ASSERT(pip);
     _sg_init_pipeline(pip);
     _sg_init_pipeline(pip);
 }
 }
 
 
-static void _sg_create_pass(_sg_pass* pass, const sg_pass_desc* desc) {
+static void _sg_create_pass(_sg_backend* state, _sg_pass* pass, const sg_pass_desc* desc) {
     SOKOL_ASSERT(pass && desc);
     SOKOL_ASSERT(pass && desc);
+    SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_ALLOC);
     /* FIXME */
     /* FIXME */
     pass->slot.state = SG_RESOURCESTATE_FAILED;
     pass->slot.state = SG_RESOURCESTATE_FAILED;
 }
 }

+ 14 - 8
sokol_gfx.h

@@ -155,6 +155,7 @@ typedef enum {
     SG_PIXELFORMAT_PVRTC2_RGB,
     SG_PIXELFORMAT_PVRTC2_RGB,
     SG_PIXELFORMAT_PVRTC4_RGB,
     SG_PIXELFORMAT_PVRTC4_RGB,
     SG_PIXELFORMAT_PVRTC2_RGBA,
     SG_PIXELFORMAT_PVRTC2_RGBA,
+    SG_PIXELFORMAT_PVRTC4_RGBA,
     SG_PIXELFORMAT_ETC2_RGB8,
     SG_PIXELFORMAT_ETC2_RGB8,
     SG_PIXELFORMAT_ETC2_SRGB8,
     SG_PIXELFORMAT_ETC2_SRGB8,
 } sg_pixel_format;
 } sg_pixel_format;
@@ -177,9 +178,9 @@ typedef enum {
 } sg_filter;
 } sg_filter;
 
 
 typedef enum {
 typedef enum {
-    SG_CLAMP_TO_EDGE,
-    SG_REPEAT,
-    SG_MIRRORED_REPEAT,
+    SG_WRAP_CLAMP_TO_EDGE,
+    SG_WRAP_REPEAT,
+    SG_WRAP_MIRRORED_REPEAT,
 } sg_wrap;
 } sg_wrap;
 
 
 typedef enum {
 typedef enum {
@@ -389,17 +390,22 @@ typedef struct {
     uint32_t _init_guard;
     uint32_t _init_guard;
     sg_image_type type;
     sg_image_type type;
     bool render_target;
     bool render_target;
-    int width;
-    int height;
+    uint16_t width;
+    uint16_t height;
     union {
     union {
-        int depth;
-        int layers;
+        uint16_t depth;
+        uint16_t layers;
     };
     };
-    int num_mipmaps;
+    uint16_t num_mipmaps;
     sg_usage usage;
     sg_usage usage;
     sg_pixel_format color_format;
     sg_pixel_format color_format;
     sg_pixel_format depth_format;   /* render targets only */
     sg_pixel_format depth_format;   /* render targets only */
     int sample_count;               /* render targets only */
     int sample_count;               /* render targets only */
+    sg_filter min_filter;
+    sg_filter mag_filter;
+    sg_wrap wrap_u;
+    sg_wrap wrap_v;
+    sg_wrap wrap_w;
     int num_data_items;
     int num_data_items;
     const void** data_ptrs;
     const void** data_ptrs;
     const int* data_sizes;
     const int* data_sizes;