// Copyright 2016 The SwiftShader Authors. All Rights Reserved. // // 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. // Surface.cpp: Implements the Surface class, representing a drawing surface // such as the client area of a window, including any back buffers. #include "Surface.h" #include "main.h" #include "Display.h" #include "Image.hpp" #include "Context.h" #include "common/debug.h" #include "Main/FrameBuffer.hpp" #if defined(_WIN32) #include #endif #include namespace gl { Surface::Surface(Display *display, NativeWindowType window) : mDisplay(display), mWindow(window) { frameBuffer = 0; backBuffer = 0; mDepthStencil = nullptr; mTextureFormat = GL_NONE; mTextureTarget = GL_NONE; mSwapInterval = -1; setSwapInterval(1); } Surface::Surface(Display *display, GLint width, GLint height, GLenum textureFormat, GLenum textureType) : mDisplay(display), mWindow(nullptr), mWidth(width), mHeight(height) { frameBuffer = 0; backBuffer = 0; mDepthStencil = nullptr; mWindowSubclassed = false; mTextureFormat = textureFormat; mTextureTarget = textureType; mSwapInterval = -1; setSwapInterval(1); } Surface::~Surface() { release(); } bool Surface::initialize() { ASSERT(!frameBuffer && !backBuffer && !mDepthStencil); return reset(); } void Surface::release() { if(mDepthStencil) { mDepthStencil->release(); mDepthStencil = nullptr; } if(backBuffer) { backBuffer->release(); backBuffer = 0; } delete frameBuffer; frameBuffer = 0; } bool Surface::reset() { if(!mWindow) { return reset(mWidth, mHeight); } // FIXME: Wrap into an abstract Window class #if defined(_WIN32) RECT windowRect; GetClientRect(mWindow, &windowRect); return reset(windowRect.right - windowRect.left, windowRect.bottom - windowRect.top); #else XWindowAttributes windowAttributes; XGetWindowAttributes(mDisplay->getNativeDisplay(), mWindow, &windowAttributes); return reset(windowAttributes.width, windowAttributes.height); #endif } bool Surface::reset(int backBufferWidth, int backBufferHeight) { release(); if(mWindow) { frameBuffer = ::createFrameBuffer(mDisplay->getNativeDisplay(), mWindow, backBufferWidth, backBufferHeight); if(!frameBuffer) { ERR("Could not create frame buffer"); release(); return error(GL_OUT_OF_MEMORY, false); } } backBuffer = new Image(0, backBufferWidth, backBufferHeight, GL_RGB, GL_UNSIGNED_BYTE); if(!backBuffer) { ERR("Could not create back buffer"); release(); return error(GL_OUT_OF_MEMORY, false); } if(true) // Always provide a depth/stencil buffer { mDepthStencil = new Image(0, backBufferWidth, backBufferHeight, sw::FORMAT_D24S8, 1, false, true); if(!mDepthStencil) { ERR("Could not create depth/stencil buffer for surface"); release(); return error(GL_OUT_OF_MEMORY, false); } } mWidth = backBufferWidth; mHeight = backBufferHeight; return true; } void Surface::swap() { if(backBuffer) { void *source = backBuffer->lockInternal(0, 0, 0, sw::LOCK_READONLY, sw::PUBLIC); frameBuffer->flip(source, backBuffer->Surface::getInternalFormat(), backBuffer->getInternalPitchB()); backBuffer->unlockInternal(); checkForResize(); } } Image *Surface::getRenderTarget() { if(backBuffer) { backBuffer->addRef(); } return backBuffer; } Image *Surface::getDepthStencil() { if(mDepthStencil) { mDepthStencil->addRef(); } return mDepthStencil; } void Surface::setSwapInterval(GLint interval) { if(mSwapInterval == interval) { return; } mSwapInterval = interval; mSwapInterval = std::max(mSwapInterval, mDisplay->getMinSwapInterval()); mSwapInterval = std::min(mSwapInterval, mDisplay->getMaxSwapInterval()); } GLint Surface::getWidth() const { return mWidth; } GLint Surface::getHeight() const { return mHeight; } GLenum Surface::getTextureFormat() const { return mTextureFormat; } GLenum Surface::getTextureTarget() const { return mTextureTarget; } bool Surface::checkForResize() { #if defined(_WIN32) RECT client; if(!GetClientRect(mWindow, &client)) { ASSERT(false); return false; } int clientWidth = client.right - client.left; int clientHeight = client.bottom - client.top; #else XWindowAttributes windowAttributes; XGetWindowAttributes(mDisplay->getNativeDisplay(), mWindow, &windowAttributes); int clientWidth = windowAttributes.width; int clientHeight = windowAttributes.height; #endif bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight(); if(sizeDirty) { reset(clientWidth, clientHeight); if(getCurrentDrawSurface() == this) { getContext()->makeCurrent(this); } return true; } return false; } }