WIP: SDL/OpenGL renderer. Make mvedecode use renderer for playback.

This commit is contained in:
2015-04-30 16:16:48 +02:00
parent b9d4048695
commit 7fb5f66091
27 changed files with 1399 additions and 103 deletions

23
render/Drawable.hh Normal file
View File

@@ -0,0 +1,23 @@
#ifndef WC3RE_RENDER_DRAWABLE_HH__
#define WC3RE_RENDER_DRAWABLE_HH__
namespace render {
class Renderer;
class Drawable {
public:
Drawable(Renderer& renderer)
: renderer_(renderer) {
}
virtual ~Drawable() {
}
virtual void draw() = 0;
protected:
Renderer& renderer_;
};
}
#endif

View File

@@ -4,10 +4,25 @@
using namespace gl;
void TextureDeleter::operator()(gl::GLuint tex) const {
glDeleteTextures(1, &tex);
}
namespace render {
void TextureDeleter::operator() (gl::GLsizei count, gl::GLuint tex[]) const {
glDeleteTextures(count, tex);
void TextureDeleter::operator()(gl::GLuint tex) const {
glDeleteTextures(1, &tex);
}
void TextureDeleter::operator() (gl::GLsizei count, gl::GLuint tex[]) const {
glDeleteTextures(count, tex);
}
void ProgramDeleter::operator()(gl::GLuint prog) const {
glDeleteProgram(prog);
}
void ShaderDeleter::operator()(gl::GLuint shader) const {
glDeleteShader(shader);
}
void VertexArrayDeleter::operator()(gl::GLuint varr) const {
glDeleteVertexArrays(1, &varr);
}
}

View File

@@ -3,69 +3,90 @@
#include <glbinding/gl/types.h>
struct TextureDeleter {
void operator() (gl::GLuint tex) const;
void operator() (gl::GLsizei count, gl::GLuint tex[]) const;
};
namespace render {
struct TextureDeleter {
void operator() (gl::GLuint tex) const;
void operator() (gl::GLsizei count, gl::GLuint tex[]) const;
};
struct FramebufferDeleter {
void operator() (gl::GLuint tex) const;
};
struct FramebufferDeleter {
void operator() (gl::GLuint tex) const;
};
struct ShaderDeleter {
void operator() (gl::GLuint tex) const;
};
struct ShaderDeleter {
void operator() (gl::GLuint tex) const;
};
struct ProgramDeleter {
void operator() (gl::GLuint tex) const;
};
struct ProgramDeleter {
void operator() (gl::GLuint tex) const;
};
struct VertexArrayDeleter {
void operator() (gl::GLuint tex) const;
};
struct VertexArrayDeleter {
void operator() (gl::GLuint tex) const;
};
struct BufferDeleter {
void operator() (gl::GLuint tex) const;
};
struct BufferDeleter {
void operator() (gl::GLuint tex) const;
};
template<class Deleter, class Handle = gl::GLuint>
class GlResource {
public:
GlResource()
: handle_() {
}
template<class Deleter, class Handle = gl::GLuint>
class GlResource {
public:
GlResource()
: handle_() {
}
GlResource(Handle handle)
: handle_(handle) {
}
GlResource(Handle handle)
: handle_(handle) {
}
~GlResource() {
if (handle_)
Deleter()(handle_);
handle_ = Handle();
}
GlResource(GlResource const& copy) = delete;
GlResource& operator=(GlResource const& copy) = delete;
operator Handle() const {
return handle_;
}
GlResource(GlResource && move)
: handle_(move.handle_) {
move.handle_ = Handle();
}
Handle& get() {
return handle_;
}
GlResource& operator=(GlResource && move) {
if (handle_)
Deleter()(handle_);
handle_ = move.handle_;
move.handle_ = Handle();
}
~GlResource() {
if (handle_)
Deleter()(handle_);
}
Handle const& get() const {
return handle_;
}
operator Handle() const {
return handle_;
}
operator Handle&() {
return handle_;
}
Handle& get() {
return handle_;
}
Handle const& get() const {
return handle_;
}
private:
Handle handle_;
};
private:
Handle handle_;
};
using TextureResource = GlResource<TextureDeleter>;
using FramebufferResource = GlResource<FramebufferDeleter>;
using ShaderResource = GlResource<ShaderDeleter>;
using ProgramResource = GlResource<ProgramDeleter>;
using VertexArrayResource = GlResource<VertexArrayDeleter>;
using BufferResource = GlResource<BufferDeleter>;
using TextureResource = GlResource<TextureDeleter>;
using FramebufferResource = GlResource<FramebufferDeleter>;
using ShaderResource = GlResource<ShaderDeleter>;
using ProgramResource = GlResource<ProgramDeleter>;
using VertexArrayResource = GlResource<VertexArrayDeleter>;
using BufferResource = GlResource<BufferDeleter>;
}
#endif

97
render/Overlay.cc Normal file
View File

@@ -0,0 +1,97 @@
#include <glbinding/gl/gl.h>
#include <SDL2/SDL.h>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "Overlay.hh"
#include "renderutil.hh"
#include "VBOManager.hh"
#include "ProgramProvider.hh"
#include "Renderer.hh"
using namespace gl;
namespace render {
namespace {
struct VertexAttribs {
int16_t vertex[2];
uint16_t texCoords[2];
} __attribute__((__packed__));
}
Overlay::Overlay(Renderer& renderer, int width, int height, int left, int top, int intWidth, int intHeight)
: Drawable(renderer), texture_(create2DTexture(intWidth, intHeight, false, 1)),
vbo_(VBOManager::getInstance().alloc(sizeof(VertexAttribs)*6)),
width_(width), height_(height), top_(top), left_(left), intWidth_(intWidth), intHeight_(intHeight)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, static_cast<int>(GL_LINEAR));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, static_cast<int>(GL_CLAMP_TO_EDGE));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, static_cast<int>(GL_CLAMP_TO_EDGE));
program_ = ProgramProvider::getInstance().getProgram("overlay", "overlay");
glGenVertexArrays(1, &vertexArray_.get());
short int t = top_, l = left_, b = height+top_, r = width+left_;
std::vector<VertexAttribs> vertexAttribs{
{{l, t}, {0, 0}},
{{r, b}, {65535u, 65535u}},
{{r, t}, {65535u, 0}},
{{l, t}, {0, 0}},
{{l, b}, {0, 65535u}},
{{r, b}, {65535u, 65535u}}};
glBindBuffer(GL_ARRAY_BUFFER, vbo_.getVBOId());
glBindVertexArray(vertexArray_);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_SHORT, GL_FALSE, sizeof(VertexAttribs),
vbo_.getOfs(offsetof(VertexAttribs, vertex)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(VertexAttribs),
vbo_.getOfs(offsetof(VertexAttribs, texCoords)));
glBufferSubData(GL_ARRAY_BUFFER, vbo_.getBase(),
sizeof(VertexAttribs)*6,
vertexAttribs.data());
ovlProj_ = glm::ortho(0.0f, static_cast<float>(renderer_.getWidth()),
static_cast<float>(renderer_.getHeight()), 0.0f);
}
void Overlay::draw()
{
glDisable(GL_DEPTH_TEST);
glUseProgram(program_);
glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(ovlProj_));
glUniform1i(1, 0);
glBindTexture(GL_TEXTURE_2D, texture_);
glBindVertexArray(vertexArray_);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
void Overlay::setContent(SDL_Surface *content)
{
if (!content ||
(content->h != intHeight_) ||
(content->w != intWidth_))
throw Exception{"null or mismatched surface"};
glBindTexture(GL_TEXTURE_2D, texture_);
if (content->format->format != SDL_PIXELFORMAT_RGB24) {
SDLSurfaceUPtr tmpSurf(SDL_ConvertSurfaceFormat(content, SDL_PIXELFORMAT_RGB24, 0));
if (!tmpSurf)
throw SDLException{};
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tmpSurf->w, tmpSurf->h, GL_RGB, GL_UNSIGNED_BYTE, tmpSurf->pixels);
} else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, content->w, content->h, GL_RGB, GL_UNSIGNED_BYTE, content->pixels);
}
void Overlay::setContentRGB8(void *data)
{
glBindTexture(GL_TEXTURE_2D, texture_);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, intWidth_, intHeight_, GL_RGB, GL_UNSIGNED_BYTE, data);
}
}

33
render/Overlay.hh Normal file
View File

@@ -0,0 +1,33 @@
#ifndef WC3RE_RENDER_OVERLAY_HH__
#define WC3RE_RENDER_OVERLAY_HH__
#include <SDL2/SDL.h>
#define GLM_FORCE_RADIANS
#include <glm/glm.hpp>
#include "Drawable.hh"
#include "GlResource.hh"
#include "VBOManager.hh"
namespace render {
/* A two-dimensional surface which can be displayed */
class Overlay : public Drawable {
public:
Overlay(Renderer& renderer, int width, int height, int left, int top, int intWidth, int intHeight);
void draw() override;
void setContent(SDL_Surface *content);
void setContentRGB8(void *data);
private:
TextureResource texture_;
VertexArrayResource vertexArray_;
gl::GLuint program_;
VBOManager::VBOAlloc vbo_;
int width_, height_, top_, left_, intWidth_, intHeight_;
glm::mat4 ovlProj_;
};
}
#endif

99
render/ProgramProvider.cc Normal file
View File

@@ -0,0 +1,99 @@
#include <mutex>
#include <glbinding/gl/gl.h>
#include "ProgramProvider.hh"
#include "exceptions.hh"
#include "util.hh"
using namespace gl;
template<>
std::unique_ptr<render::ProgramProvider> Singleton<render::ProgramProvider>::instance{nullptr};
template<>
std::once_flag Singleton<render::ProgramProvider>::instance_flag{};
namespace render {
namespace {
class ShaderException : public Exception {
public:
ShaderException(std::string const& msg)
: Exception(msg)
{
}
std::string toString() const override {
return "ShaderException: " + msg_;
}
};
}
ProgramProvider::ProgramProvider()
{
}
void shaderCompile_(std::string const& shaderName, ShaderResource& shader)
{
std::string prog = fileToString("shaders/" + shaderName);
const char* const arr[] = {prog.c_str()};
glShaderSource(shader, 1, arr, NULL);
glCompileShader(shader);
int state;
glGetShaderiv(shader, GL_COMPILE_STATUS, &state);
if (state == 0) {
int logLength;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
char *log = new char[logLength];
glGetShaderInfoLog(shader, logLength, NULL, log);
std::string msg(log);
delete[] log;
throw ShaderException(msg);
}
}
GLuint ProgramProvider::getProgram(std::string const& vertexShader, std::string const& fragShader)
{
auto key = make_tuple(vertexShader, fragShader);
auto it = programCache_.find(key);
if (it != programCache_.end())
return it->second.get();
ShaderResource vs(glCreateShader(GL_VERTEX_SHADER)),
fs(glCreateShader(GL_FRAGMENT_SHADER));
shaderCompile_(vertexShader + ".vs", vs);
shaderCompile_(fragShader + ".fs", fs);
ProgramResource prog(glCreateProgram());
glAttachShader(prog, vs);
glAttachShader(prog, fs);
glLinkProgram(prog);
int state;
glGetProgramiv(prog, GL_LINK_STATUS, &state);
if (state == 0) {
int logLength;
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
char *log = new char[logLength];
glGetProgramInfoLog(prog, logLength, NULL, log);
std::string msg(log);
delete[] log;
throw ShaderException(msg);
}
glDetachShader(prog, vs);
glDetachShader(prog, fs);
std::tie(it, std::ignore) = programCache_.insert(make_pair(key, std::move(prog)));
return it->second.get();
}
}

41
render/ProgramProvider.hh Normal file
View File

@@ -0,0 +1,41 @@
#ifndef WC3RE_RENDER_PROGRAMPROVIDER_HH__
#define WC3RE_RENDER_PROGRAMPROVIDER_HH__
#include <unordered_map>
#include "Singleton.hh"
#include "GlResource.hh"
namespace render {
using ProgramProviderKeyType = std::tuple<std::string, std::string>;
}
namespace std
{
template<>
struct hash<render::ProgramProviderKeyType> {
typedef render::ProgramProviderKeyType argument_type;
typedef std::size_t result_type;
result_type operator()(argument_type const& s) const {
const result_type h1{std::hash<std::string>()(std::get<0>(s))};
const result_type h2{std::hash<std::string>()(std::get<1>(s))};
return h1 ^ h2;
}
};
}
namespace render {
class ProgramProvider : public Singleton<ProgramProvider> {
private:
ProgramProvider();
friend class Singleton<ProgramProvider>;
public:
gl::GLuint getProgram(std::string const& vertexShader, std::string const& fragShader);
private:
std::unordered_map<ProgramProviderKeyType, ProgramResource> programCache_;
};
}
#endif

View File

@@ -1,12 +1,152 @@
#include <glbinding/gl/gl.h>
#include <glbinding/Binding.h>
#include <glbinding/callbacks.h>
#include "Renderer.hh"
#include "GlResource.hh"
#include "game/GameState.hh"
#include "exceptions.hh"
using namespace gl;
Renderer::Renderer()
{
TextureResource testTex;
glGenTextures(1, &testTex.get());
namespace render {
class GLException : public Exception {
public:
GLException(GLenum err) : Exception(), err_(err) {
}
GLenum getErr() const {return err_;}
std::string errToString() const {
std::string ret;
if (err_ == GL_INVALID_ENUM)
ret += "GL_INVALID_ENUM ";
if (err_ == GL_INVALID_VALUE)
ret += "GL_INVALID_VALUE ";
if (err_ == GL_INVALID_OPERATION)
ret += "GL_INVALID_OPERATION ";
if (err_ == GL_INVALID_FRAMEBUFFER_OPERATION)
ret += "GL_INVALID_FRAMEBUFFER_OPERATION ";
if (err_ == GL_OUT_OF_MEMORY)
ret += "GL_OUT_OF_MEMORY ";
if (err_ == GL_STACK_UNDERFLOW)
ret += "GL_STACK_UNDERFLOW ";
if (err_ == GL_STACK_OVERFLOW)
ret += "GL_STACK_OVERFLOW ";
return ret;
}
std::string toString() const override {
return "GLException: " + errToString() + "(" + std::to_string(static_cast<int>(err_)) + ")";
}
private:
GLenum err_;
};
void glAfterCallback(glbinding::FunctionCall const& fc)
{
glbinding::setCallbackMask(glbinding::CallbackMask::None);
GLenum error = glGetError();
glbinding::setCallbackMask(glbinding::CallbackMask::After);
if (error != GL_NO_ERROR)
throw GLException(error);
}
Renderer::Renderer()
: sdlInit_(), window_(), context_()
{
glbinding::Binding::initialize();
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
window_.reset(SDL_CreateWindow("WC3 RE",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
0, 0,
SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_OPENGL));
if (!window_)
throw SDLException{};
context_ = SDLGLContext(window_.get());
SDL_GetWindowSize(window_.get(), &width_, &height_);
glbinding::setCallbackMask(glbinding::CallbackMask::After);
glbinding::setAfterCallback(glAfterCallback);
#ifndef NDEBUG
{
int depth, stencil, aa, major, minor;
SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth);
SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencil);
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &aa);
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major);
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor);
printf("Depth: %d, Stencil: %d, AA: %d, GL %d.%d\n",
depth, stencil, aa, major, minor);
}
#endif
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glViewport(0, 0, width_, height_);
}
void Renderer::run()
{
bool close = false;
auto last = SDL_GetTicks();
while (!close && gamestates_.size()) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch(event.type) {
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_q)
close = true;
break;
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_CLOSE)
close = true;
break;
}
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
auto now = SDL_GetTicks();
gamestates_.back()->draw(now-last);
SDL_GL_SwapWindow(window_.get());
last = now;
}
}
void Renderer::pushGS(std::unique_ptr<game::GameState> gs)
{
gamestates_.emplace_back(std::move(gs));
}
std::unique_ptr<game::GameState> Renderer::popGS()
{
auto ret = std::move(gamestates_.back());
gamestates_.pop_back();
return ret;
}
int Renderer::getWidth() const
{
return width_;
}
int Renderer::getHeight() const
{
return height_;
}
}

View File

@@ -1,14 +1,32 @@
#ifndef WC3RE_RENDER_RENDERER_HH__
#define WC3RE_RENDER_RENDERER_HH__
#include <SDL2/SDL.h>
#include "sdlutil.hh"
class Renderer {
public:
Renderer();
namespace game {
class GameState;
}
private:
SDL_Window *win;
};
namespace render {
class Renderer {
public:
Renderer();
void run();
int getWidth() const;
int getHeight() const;
void pushGS(std::unique_ptr<game::GameState> gs);
std::unique_ptr<game::GameState> popGS();
private:
SDLInit sdlInit_;
SDLWindowUPtr window_;
SDLGLContext context_;
std::vector<std::unique_ptr<game::GameState> > gamestates_;
int width_, height_;
};
}
#endif

154
render/VBOManager.cc Normal file
View File

@@ -0,0 +1,154 @@
#include <glbinding/gl/gl.h>
#include <cassert>
#include <mutex>
#include "VBOManager.hh"
using namespace gl;
template<>
std::unique_ptr<render::VBOManager> Singleton<render::VBOManager>::instance{nullptr};
template<>
std::once_flag Singleton<render::VBOManager>::instance_flag{};
namespace render {
VBOManager::VBOManager()
{
}
VBOManager::~VBOManager()
{
}
VBOManager::VBOAlloc::~VBOAlloc()
{
if(_vbo)
_vbo->free(*this);
_vbo = nullptr;
}
VBOManager::VBOAlloc::VBOAlloc(VBOAlloc&& move)
: _vbo(move._vbo), _ofs(move._ofs), _size(move._size)
{
move._vbo = nullptr;
}
VBOManager::VBOAlloc& VBOManager::VBOAlloc::operator=(VBOAlloc&& move)
{
_vbo = move._vbo;
_ofs = move._ofs;
_size = move._size;
move._vbo = nullptr;
return *this;
}
VBOManager::VBOAlloc::VBOAlloc()
: _vbo(nullptr), _ofs(0), _size(0)
{
}
VBOManager::VBOAlloc::VBOAlloc(VBO &vbo, size_t ofs, size_t size)
: _vbo(&vbo), _ofs(ofs), _size(size)
{
}
VBOManager::VBOAlloc VBOManager::alloc(size_t size, GLenum type)
{
for(auto& vbo : _vbos[type]) {
try {
return vbo.alloc(size);
} catch (AllocFailed &ex) {
continue;
}
}
_vbos[type].emplace_back((size>default_size)?size:default_size, type);
return _vbos[type].back().alloc(size);
}
VBOManager::VBO::VBO(size_t size, GLenum type)
: _bufID(0)
{
glGenBuffers(1, &_bufID);
glBindBuffer(GL_ARRAY_BUFFER, _bufID);
glBufferData(GL_ARRAY_BUFFER, size, NULL, type);
_allocs.emplace_back(_Entry{size, false});
}
VBOManager::VBO::VBO(VBO&& move)
: _bufID(move._bufID), _allocs(std::move(move._allocs))
{
move._bufID = 0;
}
VBOManager::VBO& VBOManager::VBO::operator=(VBO&& move)
{
for (auto ent : _allocs) {
assert(!ent.used);
}
if (_bufID)
glDeleteBuffers(1, &_bufID);
_bufID = move._bufID;
move._bufID = 0;
_allocs = std::move(move._allocs);
return *this;
}
VBOManager::VBO::~VBO()
{
for (auto ent : _allocs) {
assert(!ent.used);
}
if (_bufID)
glDeleteBuffers(1, &_bufID);
_bufID = 0;
}
VBOManager::VBOAlloc VBOManager::VBO::alloc(size_t size)
{
if (size%alignment != 0)
size += (alignment - (size%alignment));
size_t pos = 0;
for (auto it = _allocs.begin();it != _allocs.end();++it) {
if (!it->used && (it->size >= size)) {
size_t leftover = it->size - size;
it->used = true;
it->size = size;
if (leftover > 0)
_allocs.insert(++it, _Entry{leftover, false});
printf("DEBUG: VBO: Allocated %lu @ %lu in %u\n", size, pos, _bufID);
return VBOAlloc(*this, pos, size);
}
pos += it->size;
}
throw AllocFailed();
}
void VBOManager::VBO::free(VBOAlloc& alloc)
{
size_t pos = 0;
for (auto it = _allocs.begin();it != _allocs.end();++it) {
if (pos == alloc._ofs) {
assert(it->size == alloc._size);
printf("DEBUG: VBO: Freed %lu @ %lu in %u\n", alloc._size, pos, _bufID);
it->used = false;
if ((std::next(it) != _allocs.end()) &&
!std::next(it)->used) {
it->size += std::next(it)->size;
_allocs.erase(std::next(it));
}
if ((it != _allocs.begin()) &&
!std::prev(it)->used) {
it->size += std::prev(it)->size;
_allocs.erase(std::prev(it));
}
return;
}
pos += it->size;
}
}
}

124
render/VBOManager.hh Normal file
View File

@@ -0,0 +1,124 @@
#ifndef WC3RE_RENDER_VBOMANAGER_HH__
#define WC3RE_RENDER_VBOMANAGER_HH__
#include <map>
#include <list>
#include <memory>
#include <cassert>
#include <vector>
#include <glbinding/gl/types.h>
#include <glbinding/gl/enum.h>
#include "Singleton.hh"
namespace render {
class VBOManager : public Singleton<VBOManager> {
public:
~VBOManager();
VBOManager(VBOManager const& copy) = delete;
VBOManager& operator=(VBOManager const& copy) = delete;
private:
VBOManager();
friend class Singleton<VBOManager>;
class VBO;
static const size_t default_size = 1048576;
static const size_t alignment = 8;
static const gl::GLenum default_type = gl::GL_STATIC_DRAW;
class AllocFailed {};
public:
class VBOAlloc {
public:
~VBOAlloc();
VBOAlloc(VBOAlloc&& move);
VBOAlloc(VBOAlloc const& copy) = delete;
VBOAlloc& operator=(VBOAlloc const& copy) = delete;
VBOAlloc& operator=(VBOAlloc&& move);
VBOAlloc();
size_t getBase() const {
assert(_vbo);
return _ofs;
}
void * getOfs(size_t ofs) const {
assert(_vbo);
assert(ofs < _size);
return reinterpret_cast<void*>(_ofs + ofs);
}
size_t getSize() const {
assert(_vbo);
return _size;
}
gl::GLuint getVBOId() const {
assert(_vbo);
return _vbo->getID();
}
explicit operator bool() const noexcept {
return (_vbo != nullptr);
}
private:
VBOAlloc(VBO &vbo, size_t ofs, size_t size);
friend class VBOManager;
friend class VBO;
VBO *_vbo;
size_t _ofs, _size;
};
VBOAlloc alloc(size_t size, gl::GLenum type = default_type);
private:
class VBO {
public:
VBO(size_t size = default_size, gl::GLenum type = default_type);
VBO(VBO const& copy) = delete;
VBO& operator=(VBO const& copy) = delete;
VBO(VBO&& move);
VBO& operator=(VBO&& move);
~VBO();
VBOAlloc alloc(size_t size);
void free(VBOAlloc& alloc);
gl::GLuint getID() const {
return _bufID;
}
private:
gl::GLuint _bufID;
struct _Entry {
size_t size;
bool used;
};
std::list<_Entry> _allocs;
};
std::map<gl::GLenum, std::vector<VBO> > _vbos;
};
}
#endif

42
render/renderutil.cc Normal file
View File

@@ -0,0 +1,42 @@
#include <glbinding/gl/gl.h>
#include <SDL2/SDL.h>
#include "renderutil.hh"
using namespace gl;
namespace render {
namespace {
unsigned ilog2(unsigned in)
{
unsigned ret = 0u;
while (in >>= 1) ++ret;
return ret;
}
}
TextureResource create2DTexture(unsigned width, unsigned height, bool alpha, unsigned levels)
{
TextureResource tex;
glGenTextures(1, &tex.get());
glBindTexture(GL_TEXTURE_2D, tex);
unsigned logWidth = ilog2(width), logHeight = ilog2(height);
if (levels == 0)
levels = std::max(logWidth,logHeight)+1u;
if(SDL_GL_ExtensionSupported("GL_ARB_texture_storage"))
glTexStorage2D(GL_TEXTURE_2D, levels, alpha?GL_RGBA8:GL_RGB8, width, height);
else {
std::printf("Warning: extension GL_ARB_texture_storage not supported!\n");
for (unsigned i = 0u; i < levels; ++i) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels-1);
glTexImage2D(GL_TEXTURE_2D, i, static_cast<const int>(alpha?GL_RGBA8:GL_RGB8), width, height, 0,
alpha?GL_RGBA8:GL_RGB8, GL_UNSIGNED_BYTE, NULL);
width = std::max(1u, (width / 2u));
height = std::max(1u, (height / 2u));
}
}
return tex;
}
}

10
render/renderutil.hh Normal file
View File

@@ -0,0 +1,10 @@
#ifndef WC3RE_RENDER_RENDERUTIL_HH__
#define WC3RE_RENDER_RENDERUTIL_HH__
#include "GlResource.hh"
namespace render {
TextureResource create2DTexture(unsigned width, unsigned height, bool alpha, unsigned levels = 0);
}
#endif

141
render/sdlutil.hh Normal file
View File

@@ -0,0 +1,141 @@
#ifndef WC3RE_RENDER_SDLUTIL_HH__
#define WC3RE_RENDER_SDLUTIL_HH__
#include <memory>
#include <cstdint>
#include <SDL2/SDL.h>
#include "exceptions.hh"
namespace render {
class SDLException : public Exception {
public:
SDLException() : Exception(SDL_GetError()) {
}
std::string toString() const override {
return "SDLException: " + msg_;
}
};
// Some helpers to C++11-ify SDL
struct SDLSurfaceDeleter {
void operator()(SDL_Surface *ptr) const {
SDL_FreeSurface(ptr);
}
};
using SDLSurfaceUPtr = std::unique_ptr<SDL_Surface, SDLSurfaceDeleter>;
struct SDLWindowDeleter {
void operator()(SDL_Window *ptr) const {
SDL_DestroyWindow(ptr);
}
};
using SDLWindowUPtr = std::unique_ptr<SDL_Window, SDLWindowDeleter>;
class SDLSurfaceScopedLock {
public:
SDLSurfaceScopedLock(SDL_Surface *surf) : surf_(surf) {
if (SDL_MUSTLOCK(surf_))
if (SDL_LockSurface(surf_) != 0)
throw SDLException{};
}
SDLSurfaceScopedLock(SDLSurfaceUPtr& surf) : surf_(surf.get()) {
if (SDL_MUSTLOCK(surf_))
if (SDL_LockSurface(surf_) != 0)
throw SDLException{};
}
SDLSurfaceScopedLock(SDLSurfaceScopedLock const& copy) = delete;
SDLSurfaceScopedLock& operator=(SDLSurfaceScopedLock const& copy) = delete;
~SDLSurfaceScopedLock() {
if (surf_ && SDL_MUSTLOCK(surf_))
SDL_UnlockSurface(surf_);
}
void unlock() {
if (surf_ && SDL_MUSTLOCK(surf_))
SDL_UnlockSurface(surf_);
surf_ = nullptr;
}
private:
SDL_Surface *surf_;
};
static bool operator==(SDL_Color const& a, SDL_Color const& b) {
return ((a.r == b.r) && (a.g == b.g) && (a.b == b.b) && (a.a == b.a));
}
static bool operator!=(SDL_Color const& a, SDL_Color const& b) {
return ((a.r != b.r) || (a.g != b.g) || (a.b != b.b) || (a.a != b.a));
}
// RAII wrapper for SDL_Init
class SDLInit {
public:
SDLInit(uint32_t flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO) {
if (SDL_Init(flags) < 0)
throw SDLException{};
}
SDLInit(SDLInit const& copy) = delete;
SDLInit& operator=(SDLInit const& copy) = delete;
~SDLInit() {
SDL_Quit();
}
};
// RAII wrapper for SDL_GLContext
class SDLGLContext {
public:
SDLGLContext(SDL_Window *win)
: context_(SDL_GL_CreateContext(win)) {
if (!context_)
throw SDLException{};
}
SDLGLContext()
: context_(nullptr) {
}
SDLGLContext(SDLGLContext const& copy) = delete;
SDLGLContext& operator=(SDLGLContext const& copy) = delete;
SDLGLContext(SDLGLContext && move):
context_(move.context_) {
move.context_ = nullptr;
}
SDLGLContext& operator=(SDLGLContext && move) {
if (context_)
SDL_GL_DeleteContext(context_);
context_ = move.context_;
move.context_ = nullptr;
return *this;
}
~SDLGLContext() {
if (context_)
SDL_GL_DeleteContext(context_);
}
operator SDL_GLContext() const {
return context_;
}
private:
SDL_GLContext context_;
};
}
#endif