blob: dd198f7cd6ed88f102dc5609237b4bad9d3604ce [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "../gfx_api.h"
#include "../renderer.h"
#include <gapic/log.h>
#include <cstring>
#include <X11/Xresource.h>
namespace gapir {
namespace {
typedef XID GLXPbuffer;
typedef XID GLXDrawable;
typedef /*struct __GLXcontextRec*/ void *GLXContext;
typedef /*struct __GLXFBConfigRec*/ void *GLXFBConfig;
enum {
// Used by glXChooseFBConfig.
GLX_RED_SIZE = 8,
GLX_GREEN_SIZE = 9,
GLX_BLUE_SIZE = 10,
GLX_ALPHA_SIZE = 11,
GLX_DEPTH_SIZE = 12,
GLX_STENCIL_SIZE = 13,
GLX_DRAWABLE_TYPE = 0x8010,
GLX_RENDER_TYPE = 0x8011,
GLX_RGBA_BIT = 0x00000001,
GLX_PBUFFER_BIT = 0x00000004,
// Used by glXCreateNewContext.
GLX_RGBA_TYPE = 0x8014,
// Used by glXCreatePbuffer.
GLX_PBUFFER_HEIGHT = 0x8040,
GLX_PBUFFER_WIDTH = 0x8041
};
extern "C" {
GLXFBConfig *glXChooseFBConfig(Display *dpy, int screen, const int *attrib_list, int *nelements);
GLXContext glXCreateNewContext(Display *dpy, GLXFBConfig config, int render_type,
GLXContext share_list, Bool direct);
GLXPbuffer glXCreatePbuffer(Display *dpy, GLXFBConfig config, const int *attrib_list);
void glXDestroyPbuffer(Display *dpy, GLXPbuffer pbuf);
Bool glXMakeContextCurrent(Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
Bool glXQueryVersion(Display *dpy, int *maj, int *min);
void glXDestroyContext(Display *dpy, GLXContext ctx);
} // extern "C"
class RendererImpl : public Renderer {
public:
RendererImpl();
virtual ~RendererImpl() override;
virtual void setBackbuffer(int width, int height, int depthSize, int stencilSize);
virtual void bind() override;
virtual void unbind() override;
virtual const char* name() override;
virtual const char* extensions() override;
virtual const char* vendor() override;
virtual const char* version() override;
private:
void reset();
void createPbuffer(int width, int height);
int mWidth;
int mHeight;
int mDepthSize;
int mStencilSize;
bool mBound;
Display *mDisplay;
GLXContext mContext;
GLXPbuffer mPbuffer;
GLXFBConfig mFBConfig;
};
RendererImpl::RendererImpl()
: mWidth(0)
, mHeight(0)
, mDepthSize(0)
, mStencilSize(0)
, mBound(false)
, mDisplay(nullptr)
, mContext(nullptr)
, mPbuffer(0) {
mDisplay = XOpenDisplay(nullptr);
if (mDisplay == nullptr) {
GAPID_FATAL("Unable to to open X display\n");
}
int major;
int minor;
if (!glXQueryVersion(mDisplay, &major, &minor) || (major == 1 && minor < 3)) {
GAPID_FATAL("GLX 1.3+ unsupported by X server (was %d.%d)\n", major, minor);
}
// Initialize with a default target.
setBackbuffer(8, 8, 24, 8);
}
RendererImpl::~RendererImpl() {
reset();
if (mDisplay != nullptr) {
XCloseDisplay(mDisplay);
}
}
void RendererImpl::reset() {
unbind();
if (mContext != nullptr) {
glXDestroyContext(mDisplay, mContext);
mContext = nullptr;
}
if (mPbuffer != 0) {
glXDestroyPbuffer(mDisplay, mPbuffer);
mPbuffer = 0;
}
mWidth = 0;
mHeight = 0;
mDepthSize = 0;
mStencilSize = 0;
}
void RendererImpl::createPbuffer(int width, int height) {
if (mPbuffer != 0) {
glXDestroyPbuffer(mDisplay, mPbuffer);
mPbuffer = 0;
}
const int pbufferAttribs[] = {
GLX_PBUFFER_WIDTH, width,
GLX_PBUFFER_HEIGHT, height,
None
};
mPbuffer = glXCreatePbuffer(mDisplay, mFBConfig, pbufferAttribs);
}
void RendererImpl::setBackbuffer(int width, int height, int depthSize, int stencilSize) {
if (mContext != nullptr &&
mWidth == width &&
mHeight == height &&
mDepthSize == depthSize &&
mStencilSize == stencilSize) {
// No change
return;
}
if (mContext != nullptr &&
mDepthSize == depthSize &&
mStencilSize == stencilSize) {
// Resize only
GAPID_INFO("Resizing renderer: %dx%d -> %dx%d\n", mWidth, mHeight, width, height);
createPbuffer(width, height);
glXMakeContextCurrent(mDisplay, mPbuffer, mPbuffer, mContext);
mWidth = width;
mHeight = height;
return;
}
const bool wasBound = mBound;
reset();
const int visualAttribs[] = {
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
GLX_DEPTH_SIZE, depthSize,
GLX_STENCIL_SIZE, stencilSize,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
None
};
int fbConfigsCount;
GLXFBConfig *fbConfigs = glXChooseFBConfig(
mDisplay, DefaultScreen(mDisplay), visualAttribs, &fbConfigsCount);
if (fbConfigs == nullptr) {
GAPID_FATAL("Unable to find a suitable X framebuffer config\n");
}
mFBConfig = fbConfigs[0];
XFree(fbConfigs);
mContext = glXCreateNewContext(mDisplay, mFBConfig, GLX_RGBA_TYPE, nullptr, True);
if (mContext == nullptr) {
GAPID_FATAL("Failed to create glX context\n");
}
XSync(mDisplay, False);
createPbuffer(width, height);
mWidth = width;
mHeight = height;
mDepthSize = depthSize;
mStencilSize = stencilSize;
if (wasBound) {
bind();
}
}
void RendererImpl::bind() {
if (!mBound) {
if (!glXMakeContextCurrent(mDisplay, mPbuffer, mPbuffer, mContext)) {
GAPID_FATAL("Unable to make GLX context current\n");
}
mBound = true;
// Initialize the graphics API
// TODO: Inefficient - consider moving the imports into this renderer
gfxapi::Initialize();
}
}
void RendererImpl::unbind() {
if (mBound) {
// TODO: glXMakeContextCurrent(...)
mBound = false;
}
}
const char* RendererImpl::name() {
return reinterpret_cast<const char*>(
gfxapi::glGetString(gfxapi::GLenum::GL_RENDERER));
}
const char* RendererImpl::extensions() {
return reinterpret_cast<const char*>(
gfxapi::glGetString(gfxapi::GLenum::GL_EXTENSIONS));
}
const char* RendererImpl::vendor() {
return reinterpret_cast<const char*>(
gfxapi::glGetString(gfxapi::GLenum::GL_VENDOR));
}
const char* RendererImpl::version() {
return reinterpret_cast<const char*>(
gfxapi::glGetString(gfxapi::GLenum::GL_VERSION));
}
} // anonymous namespace
Renderer* Renderer::create() {
return new RendererImpl();
}
} // namespace gapir