<commit_before>// Copyright 2011 Branan Purvine-Riley and Adam Johnson
//
// 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 "implementation.hpp"
#include "../interface.hpp"
#include "glexcept.hpp"
#include "../Material.hpp"
#include "../Drawable.hpp"
#include "../Image.hpp"

#include <boost/foreach.hpp>

Pipeline::Pipeline()
  : self(new PipelineImpl)
{
  // Get information on FSAA availability
  int dsamples, csamples;
  GL_CHECK(glGetIntegerv(GL_MAX_DEPTH_TEXTURE_SAMPLES, &dsamples));
  GL_CHECK(glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &csamples));
  int fsaa = std::min(dsamples, csamples);
  self->cur_fsaa = fsaa;
  do {
    self->fsaa_levels.insert(fsaa);
    fsaa -= 2;
  } while(fsaa > 1);
  if(fsaa == 0) // there's no way we'll get a 1 if we start with an even number of FSAA samples
    self->fsaa_levels.insert(1);

  // Enable some standard state
  GL_CHECK(glEnable(GL_MULTISAMPLE));
  GL_CHECK(glEnable(GL_FRAMEBUFFER_SRGB));
  GL_CHECK(glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE));
  GL_CHECK(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
  GL_CHECK(glClearColor(0.8f, 0.8f, 0.9f, 1.0f));
}

Pipeline::~Pipeline()
{}

void Pipeline::addDrawTask(DrawableMesh* mesh, Material* mat, glm::mat4 mv, RenderPass pass)
{
  DrawTaskObject d;
  d.mesh = mesh;
  d.mat = mat;
  auto i = self->tasks[pass].find(d);
  if(i != self->tasks[pass].end()) {
    i->second.transforms.push_back(mv);
  } else {
    DrawTaskData dt;
    dt.transforms.push_back(mv);
    self->tasks[pass].insert(std::make_pair(d, dt));
  }
}

void Pipeline::render()
{
  if(!self->current_framebuffer)
    return; // Skip rendering if there is no framebuffer
  GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, self->current_framebuffer->gbuffer_id));
  GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
  self->doRenderPass(Pipeline::PassStandard);
  GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, self->current_framebuffer->lbuffer_id));
  GL_CHECK(glClear(GL_COLOR_BUFFER_BIT));
  // TODO: loop through lights and fill the lbuffer
  self->doRenderPass(Pipeline::PassPostLighting);
  self->current_framebuffer->dirty = true;
}

void Pipeline::endFrame()
{
  GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, self->current_framebuffer->lbuffer_id));
  self->doRenderPass(Pipeline::PassPostEffect);
  GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
  GL_CHECK(glBlitFramebuffer(0, 0, self->current_framebuffer->width, self->current_framebuffer->height, 0, 0, self->width, self->height, GL_COLOR_BUFFER_BIT, GL_NEAREST));
}

// Here's our render target documentation!
// R,G,B,A = color
// X,Y,Z = Normal components
// S = Specular Blend
// Em = emission
// Se = specular exponent
// 0: [R][G][B][A]
// 1: [X [Y][Z][S]
// 2: [Em  ][Se  ]
//
// This is also (shockingly enough) the packing that the default material shader will expect for texture files
// Alpha is stored even though we'll never use it directly, because it's needed for alpha-to-coverage support
RenderTarget* Pipeline::createRenderTarget(uint32_t width, uint32_t height, bool mipmap)
{
  GLuint texids[5];
  GLuint fboids[2];
  glGenFramebuffers(2, fboids);
  glGenTextures(5, texids);
  RenderTarget rt;
  rt.depth_id = texids[0];
  rt.color_id = texids[1];
  rt.normal_id = texids[2];
  rt.matprop_id = texids[3];
  rt.lighting_id = texids[4];
  rt.lbuffer_id = fboids[0];
  rt.gbuffer_id = fboids[1];

  rt.build_mips = mipmap;
  rt.dirty = true;

  rt.width = width;
  rt.height = height;

  GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, rt.lbuffer_id));
  GL_CHECK(glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, rt.lighting_id));
  GL_CHECK(glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, self->cur_fsaa, GL_RGBA16F, width, height, GL_FALSE));
  GL_CHECK(glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, rt.depth_id));
  GL_CHECK(glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, self->cur_fsaa, GL_DEPTH24_STENCIL8, width, height, GL_FALSE));
  GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, rt.lighting_id, 0));
  GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, rt.depth_id, 0));
  GL_CHECK(glViewport(0, 0, width, height));
  FBO_CHECK;

  GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, rt.gbuffer_id));
  GL_CHECK(glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, rt.color_id));
  GL_CHECK(glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, self->cur_fsaa, GL_RGBA8, width, height, GL_FALSE));
  GL_CHECK(glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, rt.normal_id));
  GL_CHECK(glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, self->cur_fsaa, GL_RGBA8, width, height, GL_FALSE));
  GL_CHECK(glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, rt.matprop_id));
  GL_CHECK(glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, self->cur_fsaa, GL_RG16F, width, height, GL_FALSE));
  GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, rt.color_id, 0));
  GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D_MULTISAMPLE, rt.normal_id, 0));
  GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D_MULTISAMPLE, rt.matprop_id, 0));
  GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, rt.depth_id, 0));
  GL_CHECK(glViewport(0, 0, width, height));
  FBO_CHECK;

  RenderTarget* result = new RenderTarget;
  *result = rt;
  return result;
}

void Pipeline::destroyRenderTarget(RenderTarget* rt)
{
  GLuint texids[5];
  GLuint fboids[2];
  texids[0] = rt->depth_id;
  texids[1] = rt->color_id;
  texids[2] = rt->normal_id;
  texids[3] = rt->matprop_id;
  texids[4] = rt->lighting_id;
  fboids[0] = rt->lbuffer_id;
  fboids[1] = rt->gbuffer_id;
  GL_CHECK(glDeleteTextures(4, texids));
  GL_CHECK(glDeleteFramebuffers(2, fboids));
  delete rt;
}

void Pipeline::setRenderTarget(RenderTarget* rt)
{
  self->current_framebuffer = rt;
}

void Pipeline::setViewport(uint32_t width, uint32_t height)
{
  self->width = width;
  self->height = height;
  GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
  GL_CHECK(glViewport(0, 0, width, height));
}

Loader* Pipeline::createLoader()
{
  return new Loader;
}

void PipelineImpl::doRenderPass(Pipeline::RenderPass pass)
{
  auto end = tasks[pass].end();
  for(auto i = tasks[pass].begin(); i != end; ++i) {
    DrawTaskObject dto = i->first;
    DrawTaskData dtd = i->second;

    // break out if the data isn't fully loaded
    if(!dto.mat->shaders || !dto.mesh->buffer)
      continue;

    // bind the shader and vertex array
    GL_CHECK(glUseProgram(dto.mat->shaders->gl_id));
    GL_CHECK(glBindVertexArray(dto.mesh->buffer->vao));

    // loop over the uniforms. Set aside the modelview matrix if found.
    GLint mv_id = -1;
    GLuint current_tex = 0;
    auto uend = dto.mat->uniforms.end();
    for(auto j = dto.mat->uniforms.begin(); j != uend; j++) {
      GLuint uid = boost::any_cast<int>(j->pipe_id);
      switch(j->type) {
      case UniformDef::Texture:
        {
          Image *img = boost::any_cast<Image*>(j->value);
          GL_CHECK(glActiveTexture(GL_TEXTURE0+current_tex));
          if(img->tex) {
            GL_CHECK(glBindTexture(GL_TEXTURE_2D, img->tex->id));
          } else {
            GL_CHECK(glBindTexture(GL_TEXTURE_2D, 0));
          }
          GL_CHECK(glUniform1i(uid, current_tex));
          current_tex++;
          break;
        }
      case UniformDef::ModelView:
        {
          mv_id = uid;
          break;
        }
      case UniformDef::BoneMatrices:
        {
          size_t mat_count = dtd.transforms.size();
          if(mat_count > SENSE_MAX_VTX_BONES)
            mat_count = SENSE_MAX_VTX_BONES;
          glUniformMatrix4fv(uid, mat_count, GL_FALSE, (GLfloat*)(&(dtd.transforms[0])));
          break;
        }
      case UniformDef::DepthInfo:
      case UniformDef::LightColor:
      case UniformDef::LightPosition:
      case UniformDef::LightRadius:
      case UniformDef::Projection:
      case UniformDef::Webview:
      default:
        throw std::runtime_error("Tried to use unimplemenented uniform type");
      }
    }
    if(mv_id != -1) {
      // we have a model-view matrix array handle. We need to draw one or more instance passes now.
      size_t cur_transform = 0;
      size_t remaining_mvs = dtd.transforms.size();
      do {
        size_t batch_size = remaining_mvs <= SENSE_MAX_INSTANCES ? remaining_mvs : SENSE_MAX_INSTANCES;
        GLfloat* data_ptr = (GLfloat*)&dtd.transforms[cur_transform];
        GL_CHECK(glUniformMatrix4fv(mv_id, batch_size, GL_FALSE, data_ptr));
        if(dto.mesh->index_data) {
          GL_CHECK(glDrawElementsInstanced(GL_TRIANGLES, dto.mesh->index_count, dto.mesh->buffer->idx_type, 0, batch_size));
        } else {
          GL_CHECK(glDrawArraysInstanced(GL_TRIANGLES, 0, dto.mesh->data_size / dto.mesh->data_stride, batch_size));
        }
        cur_transform += batch_size;
        remaining_mvs -= batch_size;
      } while (remaining_mvs);
    } else {
      if(dto.mesh->index_data) {
        GL_CHECK(glDrawElements(GL_TRIANGLES, dto.mesh->index_count, dto.mesh->buffer->idx_type, 0));
      } else {
        GL_CHECK(glDrawArrays(GL_TRIANGLES, 0, dto.mesh->data_size / dto.mesh->data_stride));
      }
    }
  }
}
<commit_msg>Fix another null check<commit_after>// Copyright 2011 Branan Purvine-Riley and Adam Johnson
//
// 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 "implementation.hpp"
#include "../interface.hpp"
#include "glexcept.hpp"
#include "../Material.hpp"
#include "../Drawable.hpp"
#include "../Image.hpp"

#include <boost/foreach.hpp>

Pipeline::Pipeline()
  : self(new PipelineImpl)
{
  // Get information on FSAA availability
  int dsamples, csamples;
  GL_CHECK(glGetIntegerv(GL_MAX_DEPTH_TEXTURE_SAMPLES, &dsamples));
  GL_CHECK(glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &csamples));
  int fsaa = std::min(dsamples, csamples);
  self->cur_fsaa = fsaa;
  do {
    self->fsaa_levels.insert(fsaa);
    fsaa -= 2;
  } while(fsaa > 1);
  if(fsaa == 0) // there's no way we'll get a 1 if we start with an even number of FSAA samples
    self->fsaa_levels.insert(1);

  // Enable some standard state
  GL_CHECK(glEnable(GL_MULTISAMPLE));
  GL_CHECK(glEnable(GL_FRAMEBUFFER_SRGB));
  GL_CHECK(glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE));
  GL_CHECK(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
  GL_CHECK(glClearColor(0.8f, 0.8f, 0.9f, 1.0f));
}

Pipeline::~Pipeline()
{}

void Pipeline::addDrawTask(DrawableMesh* mesh, Material* mat, glm::mat4 mv, RenderPass pass)
{
  DrawTaskObject d;
  d.mesh = mesh;
  d.mat = mat;
  auto i = self->tasks[pass].find(d);
  if(i != self->tasks[pass].end()) {
    i->second.transforms.push_back(mv);
  } else {
    DrawTaskData dt;
    dt.transforms.push_back(mv);
    self->tasks[pass].insert(std::make_pair(d, dt));
  }
}

void Pipeline::render()
{
  if(!self->current_framebuffer)
    return; // Skip rendering if there is no framebuffer
  GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, self->current_framebuffer->gbuffer_id));
  GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
  self->doRenderPass(Pipeline::PassStandard);
  GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, self->current_framebuffer->lbuffer_id));
  GL_CHECK(glClear(GL_COLOR_BUFFER_BIT));
  // TODO: loop through lights and fill the lbuffer
  self->doRenderPass(Pipeline::PassPostLighting);
  self->current_framebuffer->dirty = true;
}

void Pipeline::endFrame()
{
  GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, self->current_framebuffer->lbuffer_id));
  self->doRenderPass(Pipeline::PassPostEffect);
  GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
  GL_CHECK(glBlitFramebuffer(0, 0, self->current_framebuffer->width, self->current_framebuffer->height, 0, 0, self->width, self->height, GL_COLOR_BUFFER_BIT, GL_NEAREST));
}

// Here's our render target documentation!
// R,G,B,A = color
// X,Y,Z = Normal components
// S = Specular Blend
// Em = emission
// Se = specular exponent
// 0: [R][G][B][A]
// 1: [X [Y][Z][S]
// 2: [Em  ][Se  ]
//
// This is also (shockingly enough) the packing that the default material shader will expect for texture files
// Alpha is stored even though we'll never use it directly, because it's needed for alpha-to-coverage support
RenderTarget* Pipeline::createRenderTarget(uint32_t width, uint32_t height, bool mipmap)
{
  GLuint texids[5];
  GLuint fboids[2];
  glGenFramebuffers(2, fboids);
  glGenTextures(5, texids);
  RenderTarget rt;
  rt.depth_id = texids[0];
  rt.color_id = texids[1];
  rt.normal_id = texids[2];
  rt.matprop_id = texids[3];
  rt.lighting_id = texids[4];
  rt.lbuffer_id = fboids[0];
  rt.gbuffer_id = fboids[1];

  rt.build_mips = mipmap;
  rt.dirty = true;

  rt.width = width;
  rt.height = height;

  GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, rt.lbuffer_id));
  GL_CHECK(glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, rt.lighting_id));
  GL_CHECK(glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, self->cur_fsaa, GL_RGBA16F, width, height, GL_FALSE));
  GL_CHECK(glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, rt.depth_id));
  GL_CHECK(glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, self->cur_fsaa, GL_DEPTH24_STENCIL8, width, height, GL_FALSE));
  GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, rt.lighting_id, 0));
  GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, rt.depth_id, 0));
  GL_CHECK(glViewport(0, 0, width, height));
  FBO_CHECK;

  GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, rt.gbuffer_id));
  GL_CHECK(glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, rt.color_id));
  GL_CHECK(glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, self->cur_fsaa, GL_RGBA8, width, height, GL_FALSE));
  GL_CHECK(glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, rt.normal_id));
  GL_CHECK(glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, self->cur_fsaa, GL_RGBA8, width, height, GL_FALSE));
  GL_CHECK(glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, rt.matprop_id));
  GL_CHECK(glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, self->cur_fsaa, GL_RG16F, width, height, GL_FALSE));
  GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, rt.color_id, 0));
  GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D_MULTISAMPLE, rt.normal_id, 0));
  GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D_MULTISAMPLE, rt.matprop_id, 0));
  GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, rt.depth_id, 0));
  GL_CHECK(glViewport(0, 0, width, height));
  FBO_CHECK;

  RenderTarget* result = new RenderTarget;
  *result = rt;
  return result;
}

void Pipeline::destroyRenderTarget(RenderTarget* rt)
{
  GLuint texids[5];
  GLuint fboids[2];
  texids[0] = rt->depth_id;
  texids[1] = rt->color_id;
  texids[2] = rt->normal_id;
  texids[3] = rt->matprop_id;
  texids[4] = rt->lighting_id;
  fboids[0] = rt->lbuffer_id;
  fboids[1] = rt->gbuffer_id;
  GL_CHECK(glDeleteTextures(4, texids));
  GL_CHECK(glDeleteFramebuffers(2, fboids));
  delete rt;
}

void Pipeline::setRenderTarget(RenderTarget* rt)
{
  self->current_framebuffer = rt;
}

void Pipeline::setViewport(uint32_t width, uint32_t height)
{
  self->width = width;
  self->height = height;
  GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
  GL_CHECK(glViewport(0, 0, width, height));
}

Loader* Pipeline::createLoader()
{
  return new Loader;
}

void PipelineImpl::doRenderPass(Pipeline::RenderPass pass)
{
  auto end = tasks[pass].end();
  for(auto i = tasks[pass].begin(); i != end; ++i) {
    DrawTaskObject dto = i->first;
    DrawTaskData dtd = i->second;

    // break out if the data isn't fully loaded
    if(!dto.mat->shaders || !dto.mesh->buffer || !dto.mesh->buffer->vao)
      continue;

    // bind the shader and vertex array
    GL_CHECK(glUseProgram(dto.mat->shaders->gl_id));
    GL_CHECK(glBindVertexArray(dto.mesh->buffer->vao));

    // loop over the uniforms. Set aside the modelview matrix if found.
    GLint mv_id = -1;
    GLuint current_tex = 0;
    auto uend = dto.mat->uniforms.end();
    for(auto j = dto.mat->uniforms.begin(); j != uend; j++) {
      GLuint uid = boost::any_cast<int>(j->pipe_id);
      switch(j->type) {
      case UniformDef::Texture:
        {
          Image *img = boost::any_cast<Image*>(j->value);
          GL_CHECK(glActiveTexture(GL_TEXTURE0+current_tex));
          if(img->tex) {
            GL_CHECK(glBindTexture(GL_TEXTURE_2D, img->tex->id));
          } else {
            GL_CHECK(glBindTexture(GL_TEXTURE_2D, 0));
          }
          GL_CHECK(glUniform1i(uid, current_tex));
          current_tex++;
          break;
        }
      case UniformDef::ModelView:
        {
          mv_id = uid;
          break;
        }
      case UniformDef::BoneMatrices:
        {
          size_t mat_count = dtd.transforms.size();
          if(mat_count > SENSE_MAX_VTX_BONES)
            mat_count = SENSE_MAX_VTX_BONES;
          glUniformMatrix4fv(uid, mat_count, GL_FALSE, (GLfloat*)(&(dtd.transforms[0])));
          break;
        }
      case UniformDef::DepthInfo:
      case UniformDef::LightColor:
      case UniformDef::LightPosition:
      case UniformDef::LightRadius:
      case UniformDef::Projection:
      case UniformDef::Webview:
      default:
        throw std::runtime_error("Tried to use unimplemenented uniform type");
      }
    }
    if(mv_id != -1) {
      // we have a model-view matrix array handle. We need to draw one or more instance passes now.
      size_t cur_transform = 0;
      size_t remaining_mvs = dtd.transforms.size();
      do {
        size_t batch_size = remaining_mvs <= SENSE_MAX_INSTANCES ? remaining_mvs : SENSE_MAX_INSTANCES;
        GLfloat* data_ptr = (GLfloat*)&dtd.transforms[cur_transform];
        GL_CHECK(glUniformMatrix4fv(mv_id, batch_size, GL_FALSE, data_ptr));
        if(dto.mesh->index_data) {
          GL_CHECK(glDrawElementsInstanced(GL_TRIANGLES, dto.mesh->index_count, dto.mesh->buffer->idx_type, 0, batch_size));
        } else {
          GL_CHECK(glDrawArraysInstanced(GL_TRIANGLES, 0, dto.mesh->data_size / dto.mesh->data_stride, batch_size));
        }
        cur_transform += batch_size;
        remaining_mvs -= batch_size;
      } while (remaining_mvs);
    } else {
      if(dto.mesh->index_data) {
        GL_CHECK(glDrawElements(GL_TRIANGLES, dto.mesh->index_count, dto.mesh->buffer->idx_type, 0));
      } else {
        GL_CHECK(glDrawArrays(GL_TRIANGLES, 0, dto.mesh->data_size / dto.mesh->data_stride));
      }
    }
  }
}
<|endoftext|>


<commit_before>#include <core/game_state_eval.hpp>
#include <util/constants.hpp>

// Declarations of all feature functions
int PileHeight(GameState const& state);
int Holes(GameState const& state);
int ConnectedHoles(GameState const& state);
int RemovedRows(GameState const& state); // TODO
int AltitudeDifference(GameState const& state);
int MaxWellDepth(GameState const& state);
int SumOfAllWells(GameState const& state);  // TODO
int LandingHeight(GameState const& state);  // TODO
int Blocks(GameState const& state);
int WeightedBlocks(GameState const& state);
int RowTransitions(GameState const& state); // TODO
int ColTransitions(GameState const& state); // TODO
int HighestHole(GameState const& state);
int BlocksAboveHighestHole(GameState const& state);
int PotentialRows(GameState const& state);  // TODO
int Smoothness(GameState const& state);
int ErodedPieces(GameState const& state);   // TODO
int RowHoles(GameState const& state);   // TODO
int HoleDepth(GameState const& state);  // TODO

int GetVarCount() {
    return 19;
}

HarmonyRanges const* GetRanges() {
    HarmonyRanges* ranges = new HarmonyRanges();
    for (int i = 0; i < GetVarCount(); ++i)
        ranges->push_back(std::pair<float, float>(-100.0, 100.0));
    return ranges;
}

float EvaluateMove(GameState const& state, Harmony const& h) {
    return 0.0;
}

/* *
 * Function PileHeight
 *
 * Row of the topmost occupied square on the board.
 * */
int PileHeight(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    int minWellDepth = ROWS;
    for (int i = 0; i < COLS; ++i)
        minWellDepth = std::min(minWellDepth, board.WellDepth(i));
    return (ROWS - minWellDepth);
}

/* *
 * Function Holes
 *
 * The number of gaps with at least one occupied cell above them.
 * */
int Holes(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    BoardDesc const& desc = board.GetBoardDesc();
    int holeCount = 0;
    for (int i = 0; i < COLS; ++i) {
        bool hitTop = false;
        for (int j = 0; j < ROWS; ++j) {
            if (desc[i][j] && !hitTop)
                hitTop = true;
            else if (!desc[i][j] && hitTop)
                ++holeCount;
        }
    }
    return holeCount;
}

/* *
 * Function ConnectedHoles
 *
 * The number of connected gaps with at least one occupied cell above them.
 * */
int ConnectedHoles(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    BoardDesc const& desc = board.GetBoardDesc();
    int holeCount = 0;
    for (int i = 0; i < COLS; ++i) {
        bool hitTop = false;
        bool inHole = false;
        for (int j = 0; j < ROWS; ++j) {
            if (desc[i][j] && !hitTop)
                hitTop = true;
            else if (desc[i][j] && hitTop && inHole)
                inHole = false;
            else if (!desc[i][j] && hitTop && !inHole) {
                ++holeCount;
                inHole = true;
            }
        }
    }
    return holeCount;
}

/* *
 * Function RemovedRows
 *
 * The number of rows cleared by the last step.
 * */
int RemovedRows(GameState const& state) {
    std::vector<int> const& cleared = state.LastClearedRows();
    return cleared.size();
}

int AltitudeDifference(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    int minWellDepth = ROWS;
    int maxWellDepth = 0;
    for (int i = 0; i < COLS; ++i) {
        int wellDepth = board.WellDepth(i);
        minWellDepth = std::min(minWellDepth, wellDepth);
        maxWellDepth = std::min(maxWellDepth, wellDepth);
    }
    return ((ROWS - minWellDepth) - (ROWS - maxWellDepth));
}

int MaxWellDepth(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    int maxWellDepth = 0;
    for (int i = 0; i < COLS; ++i)
        maxWellDepth = std::min(maxWellDepth, board.WellDepth(i));
    return (ROWS - maxWellDepth);
}

int SumOfAllWells(GameState const& state) {
    // Sum of all wells based on adjacent columns
    return 0;
}

int LandingHeight(GameState const& state) {
    // Height at which last tetronimo was placed
    return 0;
}

int Blocks(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    BoardDesc const& desc = board.GetBoardDesc();
    int blockCount = 0;
    for (int j = 0; j < ROWS; ++j) {
        for (int i = 0; i < COLS; ++i) {
            if (desc[i][j])
                ++blockCount;
        }
    }
    return blockCount;
}

int WeightedBlocks(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    BoardDesc const& desc = board.GetBoardDesc();
    int weighted = 0;
    for (int j = 0; j < ROWS; ++j) {
        for (int i = 0; i < COLS; ++i) {
            if (desc[i][j])
                weighted += (ROWS-j);
        }
    }
    return weighted;
}

int RowTransitions(GameState const& state) {
    // Counts row transitions
    return 0;
}

int ColTransitions(GameState const& state) {
    // Counts col transitions
    return 0;
}

int HighestHole(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    BoardDesc const& desc = board.GetBoardDesc();
    int highest = 0;
    for (int i = 0; i < COLS; ++i) {
        bool hitTop = false;
        for (int j = 0; j < ROWS; ++j) {
            if (desc[i][j] && !hitTop)
                hitTop = true;
            if (!desc[i][j] && hitTop) {
                highest = std::max(highest, ROWS-j);
                break;
            }
        }
    }
    return highest;
}

int BlocksAboveHighestHole(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    BoardDesc const& desc = board.GetBoardDesc();
    int highest = 0;
    int blocksAboveCount = 0;
    for (int i = 0; i < COLS; ++i) {
        bool hitTop = false;
        int blocksHit = 0;
        for (int j = 0; j < ROWS; ++j) {
            if (desc[i][j])
                ++blocksHit;
            if (desc[i][j] && !hitTop)
                hitTop = true;
            if (!desc[i][j] && hitTop) {
                if ((ROWS-j) == highest && blocksHit > blocksAboveCount) {
                    blocksAboveCount = blocksHit;
                }
                if ((ROWS-j) > highest) {
                    highest = (ROWS-j);
                    blocksAboveCount = blocksHit;
                }
                break;
            }
        }
    }
    return blocksAboveCount;
}

int PotentialRows(GameState const& state) {
    return 0;
}

int Smoothness(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    int smoothness = 0;
    for (int i = 0; i < COLS-1; ++i)
        smoothness += abs(board.WellDepth(i)-board.WellDepth(i+1));
    return smoothness;
}

int ErodedPieces(GameState const& state) {
    return 0;
}

int RowHoles(GameState const& state) {
    return 0;
}

int HoleDepth(GameState const& state) {
    return 0;
}

<commit_msg>Implemented three more feature functions<commit_after>#include <core/game_state_eval.hpp>
#include <util/constants.hpp>

// Declarations of all feature functions
int PileHeight(GameState const& state);
int Holes(GameState const& state);
int ConnectedHoles(GameState const& state);
int RemovedRows(GameState const& state);
int AltitudeDifference(GameState const& state);
int MaxWellDepth(GameState const& state);
int SumOfAllWells(GameState const& state);  // TODO
int LandingHeight(GameState const& state);  // TODO
int Blocks(GameState const& state);
int WeightedBlocks(GameState const& state);
int RowTransitions(GameState const& state); // TODO
int ColTransitions(GameState const& state); // TODO
int HighestHole(GameState const& state);
int BlocksAboveHighestHole(GameState const& state);
int PotentialRows(GameState const& state);  // TODO
int Smoothness(GameState const& state);
int ErodedPieces(GameState const& state);   // TODO
int RowHoles(GameState const& state);   // TODO
int HoleDepth(GameState const& state);  // TODO

int GetVarCount() {
    return 19;
}

HarmonyRanges const* GetRanges() {
    HarmonyRanges* ranges = new HarmonyRanges();
    for (int i = 0; i < GetVarCount(); ++i)
        ranges->push_back(std::pair<float, float>(-100.0, 100.0));
    return ranges;
}

float EvaluateMove(GameState const& state, Harmony const& h) {
    return 0.0;
}

/* *
 * Function PileHeight
 *
 * Row of the topmost occupied square on the board.
 * */
int PileHeight(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    int minWellDepth = ROWS;
    for (int i = 0; i < COLS; ++i)
        minWellDepth = std::min(minWellDepth, board.WellDepth(i));
    return (ROWS - minWellDepth);
}

/* *
 * Function Holes
 *
 * The number of gaps with at least one occupied cell above them.
 * */
int Holes(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    BoardDesc const& desc = board.GetBoardDesc();
    int holeCount = 0;
    for (int i = 0; i < COLS; ++i) {
        bool hitTop = false;
        for (int j = 0; j < ROWS; ++j) {
            if (desc[i][j] && !hitTop)
                hitTop = true;
            else if (!desc[i][j] && hitTop)
                ++holeCount;
        }
    }
    return holeCount;
}

/* *
 * Function ConnectedHoles
 *
 * The number of connected gaps with at least one occupied cell above them.
 * */
int ConnectedHoles(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    BoardDesc const& desc = board.GetBoardDesc();
    int holeCount = 0;
    for (int i = 0; i < COLS; ++i) {
        bool hitTop = false;
        bool inHole = false;
        for (int j = 0; j < ROWS; ++j) {
            if (desc[i][j] && !hitTop)
                hitTop = true;
            else if (desc[i][j] && hitTop && inHole)
                inHole = false;
            else if (!desc[i][j] && hitTop && !inHole) {
                ++holeCount;
                inHole = true;
            }
        }
    }
    return holeCount;
}

/* *
 * Function RemovedRows
 *
 * The number of rows cleared by the last step.
 * */
int RemovedRows(GameState const& state) {
    std::vector<int> const& cleared = state.LastClearedRows();
    return cleared.size();
}

/* *
 * Function AltitudeDifference
 *
 * The height difference between the lowest and highest reachable blocks.
 * */
int AltitudeDifference(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    int minWellDepth = ROWS;
    int maxWellDepth = 0;
    for (int i = 0; i < COLS; ++i) {
        int wellDepth = board.WellDepth(i);
        minWellDepth = std::min(minWellDepth, wellDepth);
        maxWellDepth = std::min(maxWellDepth, wellDepth);
    }
    return ((ROWS - minWellDepth) - (ROWS - maxWellDepth));
}

/* *
 * Function MaxWellDepth
 *
 * Depth of the deepest well on the board.
 * */
int MaxWellDepth(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    int maxWellDepth = 0;
    int wells[COLS];
    for (int i = 0; i < COLS; ++i)
        wells[i] = board.WellDepth(i);
    if (wells[0] < wells[1]) {
        int depth = wells[1]-wells[0];
        maxWellDepth = std::max(maxWellDepth, depth);
    }
    for (int i = 1; i < COLS-1; ++i) {
        if (wells[i] < wells[i-1] && wells[i] < wells[i+1]) {
            int depth = std::min(wells[i-1]-wells[i], wells[i+1]-wells[i]);
            maxWellDepth = std::max(maxWellDepth, depth);
        }
    }
    if (wells[COLS-1] < wells[COLS-2]) {
        int depth = wells[COLS-2]-wells[COLS-1];
        maxWellDepth = std::max(maxWellDepth, depth);
    }
    return maxWellDepth;
}

/* *
 * Function SumOfAllWells
 *
 * Sum of the depths of all wells on the board.
 * */
int SumOfAllWells(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    int totalWellDepth = 0;
    int wells[COLS];
    for (int i = 0; i < COLS; ++i)
        wells[i] = board.WellDepth(i);
    if (wells[0] < wells[1]) {
        int depth = wells[1]-wells[0];
        totalWellDepth += depth;
    }
    for (int i = 1; i < COLS-1; ++i) {
        if (wells[i] < wells[i-1] && wells[i] < wells[i+1]) {
            int depth = std::min(wells[i-1]-wells[i], wells[i+1]-wells[i]);
            totalWellDepth += depth;
        }
    }
    if (wells[COLS-1] < wells[COLS-2]) {
        int depth = wells[COLS-2]-wells[COLS-1];
        totalWellDepth += depth;
    }
    return totalWellDepth;
}

int LandingHeight(GameState const& state) {
    // Height at which last tetronimo was placed
    return 0;
}

int Blocks(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    BoardDesc const& desc = board.GetBoardDesc();
    int blockCount = 0;
    for (int j = 0; j < ROWS; ++j) {
        for (int i = 0; i < COLS; ++i) {
            if (desc[i][j])
                ++blockCount;
        }
    }
    return blockCount;
}

int WeightedBlocks(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    BoardDesc const& desc = board.GetBoardDesc();
    int weighted = 0;
    for (int j = 0; j < ROWS; ++j) {
        for (int i = 0; i < COLS; ++i) {
            if (desc[i][j])
                weighted += (ROWS-j);
        }
    }
    return weighted;
}

int RowTransitions(GameState const& state) {
    // Counts row transitions
    return 0;
}

int ColTransitions(GameState const& state) {
    // Counts col transitions
    return 0;
}

int HighestHole(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    BoardDesc const& desc = board.GetBoardDesc();
    int highest = 0;
    for (int i = 0; i < COLS; ++i) {
        bool hitTop = false;
        for (int j = 0; j < ROWS; ++j) {
            if (desc[i][j] && !hitTop)
                hitTop = true;
            if (!desc[i][j] && hitTop) {
                highest = std::max(highest, ROWS-j);
                break;
            }
        }
    }
    return highest;
}

int BlocksAboveHighestHole(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    BoardDesc const& desc = board.GetBoardDesc();
    int highest = 0;
    int blocksAboveCount = 0;
    for (int i = 0; i < COLS; ++i) {
        bool hitTop = false;
        int blocksHit = 0;
        for (int j = 0; j < ROWS; ++j) {
            if (desc[i][j])
                ++blocksHit;
            if (desc[i][j] && !hitTop)
                hitTop = true;
            if (!desc[i][j] && hitTop) {
                if ((ROWS-j) == highest && blocksHit > blocksAboveCount) {
                    blocksAboveCount = blocksHit;
                }
                if ((ROWS-j) > highest) {
                    highest = (ROWS-j);
                    blocksAboveCount = blocksHit;
                }
                break;
            }
        }
    }
    return blocksAboveCount;
}

int PotentialRows(GameState const& state) {
    return 0;
}

int Smoothness(GameState const& state) {
    GameBoard const& board = state.GetBoard();
    int smoothness = 0;
    for (int i = 0; i < COLS-1; ++i)
        smoothness += abs(board.WellDepth(i)-board.WellDepth(i+1));
    return smoothness;
}

int ErodedPieces(GameState const& state) {
    return 0;
}

int RowHoles(GameState const& state) {
    return 0;
}

int HoleDepth(GameState const& state) {
    return 0;
}

<|endoftext|>


<commit_before>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  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.
 */
/*
 * Copyright (C) 2018 ScyllaDB Ltd.
 */

#pragma once

#include <atomic>
#include <memory>

#include <fmt/format.h>

#include <seastar/core/future.hh>
#include <seastar/core/future-util.hh>

using namespace seastar;

namespace perf_tests {
namespace internal {

struct config;

using clock_type = std::chrono::steady_clock;

class performance_test {
    std::string _test_case;
    std::string _test_group;

    uint64_t _single_run_iterations = 0;
    std::atomic<uint64_t> _max_single_run_iterations;
private:
    void do_run(const config&);
protected:
    [[gnu::always_inline]] [[gnu::hot]]
    bool stop_iteration() const {
        return _single_run_iterations >= _max_single_run_iterations.load(std::memory_order_relaxed);
    }

    [[gnu::always_inline]] [[gnu::hot]]
    void next_iteration() {
        _single_run_iterations++;
    }

    virtual void set_up() = 0;
    virtual void tear_down() noexcept = 0;
    virtual future<clock_type::duration> do_single_run() = 0;
public:
    performance_test(const std::string& test_case, const std::string& test_group)
        : _test_case(test_case)
        , _test_group(test_group)
    { }

    virtual ~performance_test() = default;

    const std::string& test_case() const { return _test_case; }
    const std::string& test_group() const { return _test_group; }
    std::string name() const { return fmt::format("{}.{}", test_group(), test_case()); }

    void run(const config&);
public:
    static void register_test(std::unique_ptr<performance_test>);
};

// Helper for measuring time.
// Each microbenchmark can either use the default behaviour which measures
// only the start and stop time of the whole run or manually invoke
// start_measuring_time() and stop_measuring_time() in order to measure
// only parts of each iteration.
class time_measurement {
    clock_type::time_point _run_start_time;
    clock_type::time_point _start_time;
    clock_type::duration _total_time;
public:
    [[gnu::always_inline]] [[gnu::hot]]
    void start_run() {
        _total_time = { };
        auto t = clock_type::now();
        _run_start_time = t;
        _start_time = t;
    }

    [[gnu::always_inline]] [[gnu::hot]]
    clock_type::duration stop_run() {
        auto t = clock_type::now();
        if (_start_time == _run_start_time) {
            return t - _start_time;
        }
        return _total_time;
    }

    [[gnu::always_inline]] [[gnu::hot]]
    void start_iteration() {
        _start_time = clock_type::now();
    }

    [[gnu::always_inline]] [[gnu::hot]]
    void stop_iteration() {
        auto t = clock_type::now();
        _total_time += t - _start_time;
    }
};

extern time_measurement measure_time;

namespace {

template<bool Condition, typename TrueFn, typename FalseFn>
struct do_if_constexpr_ : FalseFn {
    do_if_constexpr_(TrueFn, FalseFn false_fn) : FalseFn(std::move(false_fn)) { }
    decltype(auto) operator()() const {
        // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095
        return FalseFn::operator()(0);
    }
};
template<typename TrueFn, typename FalseFn>
struct do_if_constexpr_<true, TrueFn, FalseFn> : TrueFn {
    do_if_constexpr_(TrueFn true_fn, FalseFn) : TrueFn(std::move(true_fn)) { }
    decltype(auto) operator()() const { return TrueFn::operator()(0); }
};

template<bool Condition, typename TrueFn, typename FalseFn>
do_if_constexpr_<Condition, TrueFn, FalseFn> if_constexpr_(TrueFn&& true_fn, FalseFn&& false_fn)
{
    return do_if_constexpr_<Condition, TrueFn, FalseFn>(std::forward<TrueFn>(true_fn),
                                                        std::forward<FalseFn>(false_fn));
}

}

template<typename Test>
class concrete_performance_test final : public performance_test {
    compat::optional<Test> _test;
protected:
    virtual void set_up() override {
        _test.emplace();
    }

    virtual void tear_down() noexcept override {
        _test = compat::nullopt;
    }

    [[gnu::hot]]
    virtual future<clock_type::duration> do_single_run() override {
        // Redundant 'this->'s courtesy of https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61636
        return if_constexpr_<is_future<decltype(_test->run())>::value>([&] (auto&&...) {
            measure_time.start_run();
            return do_until([this] { return this->stop_iteration(); }, [this] {
                this->next_iteration();
                return _test->run();
            }).then([] {
                return measure_time.stop_run();
            });
        }, [&] (auto&&...) {
            measure_time.start_run();
            while (!stop_iteration()) {
                this->next_iteration();
                _test->run();
            }
            return make_ready_future<clock_type::duration>(measure_time.stop_run());
        })();
    }
public:
    using performance_test::performance_test;
};

void register_test(std::unique_ptr<performance_test>);

template<typename Test>
struct test_registrar {
    test_registrar(const std::string& test_group, const std::string& test_case) {
        auto test = std::make_unique<concrete_performance_test<Test>>(test_case, test_group);
        performance_test::register_test(std::move(test));
    }
};

}

[[gnu::always_inline]]
inline void start_measuring_time()
{
    internal::measure_time.start_iteration();
}

[[gnu::always_inline]]
inline void stop_measuring_time()
{
    internal::measure_time.stop_iteration();
}

template<typename T>
void do_not_optimize(T& v)
{
    asm volatile("" : : "r,m" (v));
}

}

#define PERF_TEST_F(test_group, test_case) \
    struct test_##test_group##_##test_case : test_group { \
        [[gnu::always_inline]] inline auto run(); \
    }; \
    static ::perf_tests::internal::test_registrar<test_##test_group##_##test_case> \
    test_##test_group##_##test_case##_registrar(#test_group, #test_case); \
    [[gnu::always_inline]] auto test_##test_group##_##test_case::run()

#define PERF_TEST(test_group, test_case) \
    struct test_##test_group##_##test_case { \
        [[gnu::always_inline]] inline auto run(); \
    }; \
    static ::perf_tests::internal::test_registrar<test_##test_group##_##test_case> \
    test_##test_group##_##test_case##_registrar(#test_group, #test_case); \
    [[gnu::always_inline]] auto test_##test_group##_##test_case::run()
<commit_msg>tests: perf: Make do_not_optimize() take the argument by const&<commit_after>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  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.
 */
/*
 * Copyright (C) 2018 ScyllaDB Ltd.
 */

#pragma once

#include <atomic>
#include <memory>

#include <fmt/format.h>

#include <seastar/core/future.hh>
#include <seastar/core/future-util.hh>

using namespace seastar;

namespace perf_tests {
namespace internal {

struct config;

using clock_type = std::chrono::steady_clock;

class performance_test {
    std::string _test_case;
    std::string _test_group;

    uint64_t _single_run_iterations = 0;
    std::atomic<uint64_t> _max_single_run_iterations;
private:
    void do_run(const config&);
protected:
    [[gnu::always_inline]] [[gnu::hot]]
    bool stop_iteration() const {
        return _single_run_iterations >= _max_single_run_iterations.load(std::memory_order_relaxed);
    }

    [[gnu::always_inline]] [[gnu::hot]]
    void next_iteration() {
        _single_run_iterations++;
    }

    virtual void set_up() = 0;
    virtual void tear_down() noexcept = 0;
    virtual future<clock_type::duration> do_single_run() = 0;
public:
    performance_test(const std::string& test_case, const std::string& test_group)
        : _test_case(test_case)
        , _test_group(test_group)
    { }

    virtual ~performance_test() = default;

    const std::string& test_case() const { return _test_case; }
    const std::string& test_group() const { return _test_group; }
    std::string name() const { return fmt::format("{}.{}", test_group(), test_case()); }

    void run(const config&);
public:
    static void register_test(std::unique_ptr<performance_test>);
};

// Helper for measuring time.
// Each microbenchmark can either use the default behaviour which measures
// only the start and stop time of the whole run or manually invoke
// start_measuring_time() and stop_measuring_time() in order to measure
// only parts of each iteration.
class time_measurement {
    clock_type::time_point _run_start_time;
    clock_type::time_point _start_time;
    clock_type::duration _total_time;
public:
    [[gnu::always_inline]] [[gnu::hot]]
    void start_run() {
        _total_time = { };
        auto t = clock_type::now();
        _run_start_time = t;
        _start_time = t;
    }

    [[gnu::always_inline]] [[gnu::hot]]
    clock_type::duration stop_run() {
        auto t = clock_type::now();
        if (_start_time == _run_start_time) {
            return t - _start_time;
        }
        return _total_time;
    }

    [[gnu::always_inline]] [[gnu::hot]]
    void start_iteration() {
        _start_time = clock_type::now();
    }

    [[gnu::always_inline]] [[gnu::hot]]
    void stop_iteration() {
        auto t = clock_type::now();
        _total_time += t - _start_time;
    }
};

extern time_measurement measure_time;

namespace {

template<bool Condition, typename TrueFn, typename FalseFn>
struct do_if_constexpr_ : FalseFn {
    do_if_constexpr_(TrueFn, FalseFn false_fn) : FalseFn(std::move(false_fn)) { }
    decltype(auto) operator()() const {
        // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095
        return FalseFn::operator()(0);
    }
};
template<typename TrueFn, typename FalseFn>
struct do_if_constexpr_<true, TrueFn, FalseFn> : TrueFn {
    do_if_constexpr_(TrueFn true_fn, FalseFn) : TrueFn(std::move(true_fn)) { }
    decltype(auto) operator()() const { return TrueFn::operator()(0); }
};

template<bool Condition, typename TrueFn, typename FalseFn>
do_if_constexpr_<Condition, TrueFn, FalseFn> if_constexpr_(TrueFn&& true_fn, FalseFn&& false_fn)
{
    return do_if_constexpr_<Condition, TrueFn, FalseFn>(std::forward<TrueFn>(true_fn),
                                                        std::forward<FalseFn>(false_fn));
}

}

template<typename Test>
class concrete_performance_test final : public performance_test {
    compat::optional<Test> _test;
protected:
    virtual void set_up() override {
        _test.emplace();
    }

    virtual void tear_down() noexcept override {
        _test = compat::nullopt;
    }

    [[gnu::hot]]
    virtual future<clock_type::duration> do_single_run() override {
        // Redundant 'this->'s courtesy of https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61636
        return if_constexpr_<is_future<decltype(_test->run())>::value>([&] (auto&&...) {
            measure_time.start_run();
            return do_until([this] { return this->stop_iteration(); }, [this] {
                this->next_iteration();
                return _test->run();
            }).then([] {
                return measure_time.stop_run();
            });
        }, [&] (auto&&...) {
            measure_time.start_run();
            while (!stop_iteration()) {
                this->next_iteration();
                _test->run();
            }
            return make_ready_future<clock_type::duration>(measure_time.stop_run());
        })();
    }
public:
    using performance_test::performance_test;
};

void register_test(std::unique_ptr<performance_test>);

template<typename Test>
struct test_registrar {
    test_registrar(const std::string& test_group, const std::string& test_case) {
        auto test = std::make_unique<concrete_performance_test<Test>>(test_case, test_group);
        performance_test::register_test(std::move(test));
    }
};

}

[[gnu::always_inline]]
inline void start_measuring_time()
{
    internal::measure_time.start_iteration();
}

[[gnu::always_inline]]
inline void stop_measuring_time()
{
    internal::measure_time.stop_iteration();
}

template<typename T>
void do_not_optimize(const T& v)
{
    asm volatile("" : : "r,m" (v));
}

}

#define PERF_TEST_F(test_group, test_case) \
    struct test_##test_group##_##test_case : test_group { \
        [[gnu::always_inline]] inline auto run(); \
    }; \
    static ::perf_tests::internal::test_registrar<test_##test_group##_##test_case> \
    test_##test_group##_##test_case##_registrar(#test_group, #test_case); \
    [[gnu::always_inline]] auto test_##test_group##_##test_case::run()

#define PERF_TEST(test_group, test_case) \
    struct test_##test_group##_##test_case { \
        [[gnu::always_inline]] inline auto run(); \
    }; \
    static ::perf_tests::internal::test_registrar<test_##test_group##_##test_case> \
    test_##test_group##_##test_case##_registrar(#test_group, #test_case); \
    [[gnu::always_inline]] auto test_##test_group##_##test_case::run()
<|endoftext|>


<commit_before>#include <exodus/exodus.h>

//for the sake of multivalue gurus new to exodus/C++ this is written
//with multivalue-mimicking "everything is a global function" syntax
//instead of exodus's native OO-style "objects and methods" syntax

var filename="myclients";

subroutine sortselect(in file, in sortselectcmd)
{

	println("\nSSELECT the data - ", sortselectcmd);

	if (!select(sortselectcmd)) {
		println("Cannot sselect");
		stop();
	}

	println("Read the data");

	var record;
	var key;

	//could also use the readnextrecord() function here
	// if we had used the new selectrecord() function above
	while (readnext(key)) {

		if (not read(record, file, key)) {
			println(key, " missing from file");
			continue;
		}
		print(key, ": ");
		println(convert(record, FM, "|"));

	}

	return;

}

program()
{

	if (not connect()) {
		println("Cannot connect to database. Please check configuration");
		stop();
	}

	var dictfilename="dict_"^ filename;

	//leave the test data files around for playing with
	var cleanup=false;
	if (cleanup) {
		deletefile(filename);
		deletefile(dictfilename);
	}

	println("\nOpen or create test file ", filename);

	var file;
	if (not open(filename, file)) {
		createfile(filename);
		if (not open(filename, file)) {
			println("Cannot open ", filename);
			stop();
		}
	}

	println("\nOpen or create the test files dictionary ", dictfilename);

	var dictfile;
	if (not open(dictfilename, dictfile)) {
		createfile(dictfilename);
		if (not open(dictfilename, file)) {
			println("Cannot open dictionary ", dictfilename);
			stop();
		}
	}

	println("\nPrepare some dictionary records");

	var dictrecs = "";
	dictrecs  =      "CLIENT_CODE |F|0|Code     ||||          ||L|8";
	dictrecs ^= FM ^ "CLIENT_NAME |F|1|Name     ||||          ||T|15";
	dictrecs ^= FM ^ "CLIENT_TYPE |F|2|Type     ||||          ||L|5";
	dictrecs ^= FM ^ "DATE_CREATED|F|3|Date     ||||D4        ||L|12";
	dictrecs ^= FM ^ "TIME_CREATED|F|4|Time     ||||MTH       ||L|12";
	dictrecs ^= FM ^ "BALANCE     |F|5|Balance  ||||MD20P     ||R|10";
	dictrecs ^= FM ^ "TIMESTAMP   |F|6|Timestamp||||[DATETIME]||L|12";
	dictrecs ^= FM ^ "@CRT        |G| |CLIENT_CODE CLIENT_NAME CLIENT_TYPE BALANCE DATE_CREATED TIME_CREATED TIMESTAMP";

	println("\nWrite the dictionary records to the dictionary");

	var nrecs=count(dictrecs, FM)+1;
	for (var recn = 1; recn <= nrecs; recn++)
	{
		var dictrec=extract(dictrecs, recn);
		var key=field(dictrec, "|", 1);
		var rec=field(dictrec, "|", 2, 9999);

		println(key, ": ", rec);
		key=trim(key);
		rec=trim(rec);
		rec=swap(rec, " |", "|");
		rec=convert(rec, "|", FM);

		write(rec, dictfile, key);

		//check we can read the record back
		var rec2;
		dictfile.outputln("dictfile");
		key.outputln("key");
		if (read(rec2,dictfile, key)) {
			if (rec2 ne rec) {
				println("record differs?!");
			}
		} else
			println("Cant read ", key, " back");
	}

	var rec;
	if (not read(rec,dictfile,"BALANCE"))
		println("Cant read BALANCE record from dictionary");

	println("\nClear the client file");
	clearfile(filename);

	println("\nPrepare some data records in a readable format");

	var recs = "";
	recs ^= FM ^ "SB001|Client AAA |A |15070|76539|1000.00|15070.76539";
	recs ^= FM ^ "JB002|Client BBB |B |15000|50539|200.00|15000.50539";
	recs ^= FM ^ "JB001|Client CCC |B |15010|60539|2000.00|15010.60539";
	recs ^= FM ^ "SB1  |Client SB1 |1 |     |     |       |           ";
	recs ^= FM ^ "JB2  |Client JB2 |2 |14000|10539|0      |14000.10539";
	recs ^= FM ^ "JB10 |Client JB10|10|14010|10539|2000.00|14010.10539";
	splicer(recs, 1, 1, "");

	println("\nWrite the data records to the data file");

	nrecs=count(recs, FM)+1;
	for (var recn = 1; recn <= nrecs; recn++)
	{
		var rec=extract(recs, recn);
		var key=field(rec, "|", 1);
		rec=field(rec, "|", 2, 9999);
		println(key, ": ", rec);
		while (index(rec, " |"))
			swapper(rec, " |", "|");
		write(trimb(convert(rec, "|", FM)), file, trim(key));
	}

	var prefix="SELECT "^ filename;

	gosub sortselect(file, prefix^ " BY CLIENT_CODE");

	gosub sortselect(file, prefix^ " BY BALANCE BY CLIENT_CODE");

	gosub sortselect(file, prefix^ " BY TIMESTAMP");

	gosub sortselect(file, prefix^ " WITH CLIENT_TYPE 'B' BY BALANCE");

	var cmd="list "^ filename^ " id-supp";
	println("\nList the file using ", quote(cmd));
	osshell(cmd);

	cmd="list "^ dictfilename^ " id-supp";
	println("\nList the dict using ", quote(cmd));
	osshell(cmd);

	if (cleanup) {
		println("\nCleaning up. Delete the files");
		deletefile(file);
		deletefile(dictfile);
	}

	println("\nJust type 'list' (without the quotes) to see it's syntax");
	println("or list dict_"^ filename^ " to see the dictionary");
}
<commit_msg>fix testsort<commit_after>#include <exodus/exodus.h>

//for the sake of multivalue gurus new to exodus/C++ this is written
//with multivalue-mimicking "everything is a global function" syntax
//instead of exodus's native OO-style "objects and methods" syntax

var filename="myclients";

subroutine sortselect(in file, in sortselectcmd)
{

	println("\nSSELECT the data - ", sortselectcmd);

	if (!select(sortselectcmd)) {
		println("Cannot sselect");
		stop();
	}

	println("Read the data");

	var record;
	var key;

	//could also use the readnextrecord() function here
	// if we had used the new selectrecord() function above
	while (readnext(key)) {

		if (not read(record, file, key)) {
			println(key, " missing from file");
			continue;
		}
		print(key, ": ");
		println(convert(record, FM, "|"));

	}

	return;

}

program()
{

	if (not connect()) {
		println("Cannot connect to database. Please check configuration");
		stop();
	}

	var dictfilename="dict_"^ filename;

	//leave the test data files around for playing with
	var cleanup=false;
	if (cleanup) {
		deletefile(filename);
		deletefile(dictfilename);
	}

	println("\nOpen or create test file ", filename);

	var file;
	if (not open(filename, file)) {
		createfile(filename);
		if (not open(filename, file)) {
			println("Cannot open ", filename);
			stop();
		}
	}

	println("\nOpen or create the test files dictionary ", dictfilename);

	var dictfile;
	if (not open(dictfilename, dictfile)) {
		createfile(dictfilename);
			println("Cannot open dictionary ", dictfilename);
			stop();
		}
	}

	println("\nPrepare some dictionary records");

	var dictrecs = "";
	dictrecs  =      "CLIENT_CODE |F|0|Code     ||||          ||L|8";
	dictrecs ^= FM ^ "CLIENT_NAME |F|1|Name     ||||          ||T|15";
	dictrecs ^= FM ^ "CLIENT_TYPE |F|2|Type     ||||          ||L|5";
	dictrecs ^= FM ^ "DATE_CREATED|F|3|Date     ||||D4        ||L|12";
	dictrecs ^= FM ^ "TIME_CREATED|F|4|Time     ||||MTH       ||L|12";
	dictrecs ^= FM ^ "BALANCE     |F|5|Balance  ||||MD20P     ||R|10";
	dictrecs ^= FM ^ "TIMESTAMP   |F|6|Timestamp||||[DATETIME]||L|12";
	dictrecs ^= FM ^ "@CRT        |G| |CLIENT_CODE CLIENT_NAME CLIENT_TYPE BALANCE DATE_CREATED TIME_CREATED TIMESTAMP";

	println("\nWrite the dictionary records to the dictionary");

	var nrecs=count(dictrecs, FM)+1;
	for (var recn = 1; recn <= nrecs; recn++)
	{
		var dictrec=extract(dictrecs, recn);
		var key=field(dictrec, "|", 1);
		var rec=field(dictrec, "|", 2, 9999);

		println(key, ": ", rec);
		key=trim(key);
		rec=trim(rec);
		rec=swap(rec, " |", "|");
		rec=convert(rec, "|", FM);

		write(rec, dictfile, key);

		//check we can read the record back
		var rec2;
		dictfile.outputln("dictfile");
		key.outputln("key");
		if (read(rec2,dictfile, key)) {
			if (rec2 ne rec) {
				println("record differs?!");
			}
		} else
			println("Cant read ", key, " back");
	}

	var rec;
	if (not read(rec,dictfile,"BALANCE"))
		println("Cant read BALANCE record from dictionary");

	println("\nClear the client file");
	clearfile(filename);

	println("\nPrepare some data records in a readable format");

	var recs = "";
	recs ^= FM ^ "SB001|Client AAA |A |15070|76539|1000.00|15070.76539";
	recs ^= FM ^ "JB002|Client BBB |B |15000|50539|200.00|15000.50539";
	recs ^= FM ^ "JB001|Client CCC |B |15010|60539|2000.00|15010.60539";
	recs ^= FM ^ "SB1  |Client SB1 |1 |     |     |       |           ";
	recs ^= FM ^ "JB2  |Client JB2 |2 |14000|10539|0      |14000.10539";
	recs ^= FM ^ "JB10 |Client JB10|10|14010|10539|2000.00|14010.10539";
	splicer(recs, 1, 1, "");

	println("\nWrite the data records to the data file");

	nrecs=count(recs, FM)+1;
	for (var recn = 1; recn <= nrecs; recn++)
	{
		var rec=extract(recs, recn);
		var key=field(rec, "|", 1);
		rec=field(rec, "|", 2, 9999);
		println(key, ": ", rec);
		while (index(rec, " |"))
			swapper(rec, " |", "|");
		write(trimb(convert(rec, "|", FM)), file, trim(key));
	}

	var prefix="SELECT "^ filename;

	gosub sortselect(file, prefix^ " BY CLIENT_CODE");

	gosub sortselect(file, prefix^ " BY BALANCE BY CLIENT_CODE");

	gosub sortselect(file, prefix^ " BY TIMESTAMP");

	gosub sortselect(file, prefix^ " WITH CLIENT_TYPE 'B' BY BALANCE");

	var cmd="list "^ filename^ " id-supp";
	println("\nList the file using ", quote(cmd));
	osshell(cmd);

	cmd="list "^ dictfilename^ " id-supp";
	println("\nList the dict using ", quote(cmd));
	osshell(cmd);

	if (cleanup) {
		println("\nCleaning up. Delete the files");
		deletefile(file);
		deletefile(dictfile);
	}

	println("\nJust type 'list' (without the quotes) to see it's syntax");
	println("or list dict_"^ filename^ " to see the dictionary");
}
<|endoftext|>


<commit_before>/*************************************************************************/
/*  image_loader_bmp.cpp                                                 */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#include "image_loader_bmp.h"

Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
		const uint8_t *p_buffer,
		const uint8_t *p_color_buffer,
		const uint32_t color_table_size,
		const bmp_header_s &p_header) {

	Error err = OK;

	if (p_buffer == NULL)
		err = FAILED;

	if (err == OK) {
		size_t index = 0;
		size_t width = (size_t)p_header.bmp_info_header.bmp_width;
		size_t height = (size_t)p_header.bmp_info_header.bmp_height;
		size_t bits_per_pixel = (size_t)p_header.bmp_info_header.bmp_bit_count;

		if (p_header.bmp_info_header.bmp_compression != BI_RGB) {
			err = FAILED;
		}
		// Check whether we can load it

		if (bits_per_pixel == 1) {
			// Requires bit unpacking...
			ERR_FAIL_COND_V(width % 8 != 0, ERR_UNAVAILABLE);
			ERR_FAIL_COND_V(height % 8 != 0, ERR_UNAVAILABLE);

		} else if (bits_per_pixel == 4) {
			// Requires bit unpacking...
			ERR_FAIL_COND_V(width % 2 != 0, ERR_UNAVAILABLE);
			ERR_FAIL_COND_V(height % 2 != 0, ERR_UNAVAILABLE);

		} else if (bits_per_pixel == 16) {

			ERR_FAIL_V(ERR_UNAVAILABLE);
		}
		if (err == OK) {

			// Image data (might be indexed)
			PoolVector<uint8_t> data;
			int data_len = 0;

			if (bits_per_pixel <= 8) { // indexed
				data_len = width * height;
			} else { // color
				data_len = width * height * 4;
			}
			ERR_FAIL_COND_V(data_len == 0, ERR_BUG);
			err = data.resize(data_len);

			PoolVector<uint8_t>::Write data_w = data.write();
			uint8_t *write_buffer = data_w.ptr();

			const uint32_t width_bytes = width * bits_per_pixel / 8;
			const uint32_t line_width = (width_bytes + 3) & ~3;

			// The actual data traversal is determined by
			// the data width in case of 8/4/1 bit images
			const uint32_t w = bits_per_pixel >= 24 ? width : width_bytes;
			const uint8_t *line = p_buffer + (line_width * (height - 1));

			for (unsigned int i = 0; i < height; i++) {
				const uint8_t *line_ptr = line;

				for (unsigned int j = 0; j < w; j++) {
					switch (bits_per_pixel) {
						case 1: {
							uint8_t color_index = *line_ptr;

							write_buffer[index + 0] = (color_index >> 7) & 1;
							write_buffer[index + 1] = (color_index >> 6) & 1;
							write_buffer[index + 2] = (color_index >> 5) & 1;
							write_buffer[index + 3] = (color_index >> 4) & 1;
							write_buffer[index + 4] = (color_index >> 3) & 1;
							write_buffer[index + 5] = (color_index >> 2) & 1;
							write_buffer[index + 6] = (color_index >> 1) & 1;
							write_buffer[index + 7] = (color_index >> 0) & 1;

							index += 8;
							line_ptr += 1;
						} break;
						case 4: {
							uint8_t color_index = *line_ptr;

							write_buffer[index + 0] = (color_index >> 4) & 0x0f;
							write_buffer[index + 1] = color_index & 0x0f;

							index += 2;
							line_ptr += 1;
						} break;
						case 8: {
							uint8_t color_index = *line_ptr;

							write_buffer[index] = color_index;

							index += 1;
							line_ptr += 1;
						} break;
						case 24: {
							uint32_t color = *((uint32_t *)line_ptr);

							write_buffer[index + 2] = color & 0xff;
							write_buffer[index + 1] = (color >> 8) & 0xff;
							write_buffer[index + 0] = (color >> 16) & 0xff;
							write_buffer[index + 3] = 0xff;

							index += 4;
							line_ptr += 3;
						} break;
						case 32: {
							uint32_t color = *((uint32_t *)line_ptr);

							write_buffer[index + 2] = color & 0xff;
							write_buffer[index + 1] = (color >> 8) & 0xff;
							write_buffer[index + 0] = (color >> 16) & 0xff;
							write_buffer[index + 3] = color >> 24;

							index += 4;
							line_ptr += 4;
						} break;
					}
				}
				line -= line_width;
			}

			if (p_color_buffer == NULL || color_table_size == 0) { // regular pixels

				p_image->create(width, height, 0, Image::FORMAT_RGBA8, data);

			} else { // data is in indexed format, extend it

				// Palette data
				PoolVector<uint8_t> palette_data;
				palette_data.resize(color_table_size * 4);

				PoolVector<uint8_t>::Write palette_data_w = palette_data.write();
				uint8_t *pal = palette_data_w.ptr();

				const uint8_t *cb = p_color_buffer;

				for (unsigned int i = 0; i < color_table_size; ++i) {
					uint32_t color = *((uint32_t *)cb);

					pal[i * 4 + 0] = (color >> 16) & 0xff;
					pal[i * 4 + 1] = (color >> 8) & 0xff;
					pal[i * 4 + 2] = (color)&0xff;
					pal[i * 4 + 3] = 0xff;

					cb += 4;
				}
				// Extend palette to image
				PoolVector<uint8_t> extended_data;
				extended_data.resize(data.size() * 4);

				PoolVector<uint8_t>::Write ex_w = extended_data.write();
				uint8_t *dest = ex_w.ptr();

				const int num_pixels = width * height;

				for (int i = 0; i < num_pixels; i++) {
					dest[0] = pal[write_buffer[i] * 4 + 0];
					dest[1] = pal[write_buffer[i] * 4 + 1];
					dest[2] = pal[write_buffer[i] * 4 + 2];
					dest[3] = pal[write_buffer[i] * 4 + 3];

					dest += 4;
				}
				p_image->create(width, height, 0, Image::FORMAT_RGBA8, extended_data);
			}
		}
	}
	return err;
}

Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f,
		bool p_force_linear, float p_scale) {

	bmp_header_s bmp_header;
	Error err = ERR_INVALID_DATA;

	// A valid bmp file should always at least have a
	// file header and a minimal info header
	if (f->get_len() > BITMAP_FILE_HEADER_SIZE + BITMAP_INFO_HEADER_MIN_SIZE) {
		// File Header
		bmp_header.bmp_file_header.bmp_signature = f->get_16();
		if (bmp_header.bmp_file_header.bmp_signature == BITMAP_SIGNATURE) {
			bmp_header.bmp_file_header.bmp_file_size = f->get_32();
			bmp_header.bmp_file_header.bmp_file_padding = f->get_32();
			bmp_header.bmp_file_header.bmp_file_offset = f->get_32();

			// Info Header
			bmp_header.bmp_info_header.bmp_header_size = f->get_32();
			ERR_FAIL_COND_V(bmp_header.bmp_info_header.bmp_header_size < BITMAP_INFO_HEADER_MIN_SIZE, ERR_FILE_CORRUPT);

			bmp_header.bmp_info_header.bmp_width = f->get_32();
			bmp_header.bmp_info_header.bmp_height = f->get_32();

			bmp_header.bmp_info_header.bmp_planes = f->get_16();
			ERR_FAIL_COND_V(bmp_header.bmp_info_header.bmp_planes != 1, ERR_FILE_CORRUPT);

			bmp_header.bmp_info_header.bmp_bit_count = f->get_16();
			bmp_header.bmp_info_header.bmp_compression = f->get_32();
			bmp_header.bmp_info_header.bmp_size_image = f->get_32();
			bmp_header.bmp_info_header.bmp_pixels_per_meter_x = f->get_32();
			bmp_header.bmp_info_header.bmp_pixels_per_meter_y = f->get_32();
			bmp_header.bmp_info_header.bmp_colors_used = f->get_32();
			bmp_header.bmp_info_header.bmp_important_colors = f->get_32();

			// Compressed bitmaps not supported, stop parsing
			if (bmp_header.bmp_info_header.bmp_compression != BI_RGB) {
				ERR_EXPLAIN("Unsupported bmp file: " + f->get_path());
				f->close();
				ERR_FAIL_V(ERR_UNAVAILABLE);
			}
			// Don't rely on sizeof(bmp_file_header) as structure padding
			// adds 2 bytes offset leading to misaligned color table reading
			uint32_t ct_offset = BITMAP_FILE_HEADER_SIZE +
								 bmp_header.bmp_info_header.bmp_header_size;
			f->seek(ct_offset);

			uint32_t color_table_size = 0;

			// bmp_colors_used may report 0 despite having a color table
			// for 4 and 1 bit images, so don't rely on this information
			if (bmp_header.bmp_info_header.bmp_bit_count <= 8) {
				// Support 256 colors max
				color_table_size = 1 << bmp_header.bmp_info_header.bmp_bit_count;
			}
			ERR_FAIL_COND_V(color_table_size == 0, ERR_BUG);

			PoolVector<uint8_t> bmp_color_table;
			// Color table is usually 4 bytes per color -> [B][G][R][0]
			bmp_color_table.resize(color_table_size * 4);
			PoolVector<uint8_t>::Write bmp_color_table_w = bmp_color_table.write();
			f->get_buffer(bmp_color_table_w.ptr(), color_table_size * 4);

			f->seek(bmp_header.bmp_file_header.bmp_file_offset);

			uint32_t bmp_buffer_size = (bmp_header.bmp_file_header.bmp_file_size -
										bmp_header.bmp_file_header.bmp_file_offset);

			PoolVector<uint8_t> bmp_buffer;
			err = bmp_buffer.resize(bmp_buffer_size);
			if (err == OK) {
				PoolVector<uint8_t>::Write bmp_buffer_w = bmp_buffer.write();
				f->get_buffer(bmp_buffer_w.ptr(), bmp_buffer_size);

				PoolVector<uint8_t>::Read bmp_buffer_r = bmp_buffer.read();
				PoolVector<uint8_t>::Read bmp_color_table_r = bmp_color_table.read();
				err = convert_to_image(p_image, bmp_buffer_r.ptr(),
						bmp_color_table_r.ptr(), color_table_size, bmp_header);
			}
			f->close();
		}
	}
	return err;
}

void ImageLoaderBMP::get_recognized_extensions(
		List<String> *p_extensions) const {

	p_extensions->push_back("bmp");
}

ImageLoaderBMP::ImageLoaderBMP() {}
<commit_msg>Fix BMP loader incorrectly interpreting color table size<commit_after>/*************************************************************************/
/*  image_loader_bmp.cpp                                                 */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#include "image_loader_bmp.h"

Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
		const uint8_t *p_buffer,
		const uint8_t *p_color_buffer,
		const uint32_t color_table_size,
		const bmp_header_s &p_header) {

	Error err = OK;

	if (p_buffer == NULL)
		err = FAILED;

	if (err == OK) {
		size_t index = 0;
		size_t width = (size_t)p_header.bmp_info_header.bmp_width;
		size_t height = (size_t)p_header.bmp_info_header.bmp_height;
		size_t bits_per_pixel = (size_t)p_header.bmp_info_header.bmp_bit_count;

		if (p_header.bmp_info_header.bmp_compression != BI_RGB) {
			err = FAILED;
		}
		// Check whether we can load it

		if (bits_per_pixel == 1) {
			// Requires bit unpacking...
			ERR_FAIL_COND_V(width % 8 != 0, ERR_UNAVAILABLE);
			ERR_FAIL_COND_V(height % 8 != 0, ERR_UNAVAILABLE);

		} else if (bits_per_pixel == 4) {
			// Requires bit unpacking...
			ERR_FAIL_COND_V(width % 2 != 0, ERR_UNAVAILABLE);
			ERR_FAIL_COND_V(height % 2 != 0, ERR_UNAVAILABLE);

		} else if (bits_per_pixel == 16) {

			ERR_FAIL_V(ERR_UNAVAILABLE);
		}
		if (err == OK) {

			// Image data (might be indexed)
			PoolVector<uint8_t> data;
			int data_len = 0;

			if (bits_per_pixel <= 8) { // indexed
				data_len = width * height;
			} else { // color
				data_len = width * height * 4;
			}
			ERR_FAIL_COND_V(data_len == 0, ERR_BUG);
			err = data.resize(data_len);

			PoolVector<uint8_t>::Write data_w = data.write();
			uint8_t *write_buffer = data_w.ptr();

			const uint32_t width_bytes = width * bits_per_pixel / 8;
			const uint32_t line_width = (width_bytes + 3) & ~3;

			// The actual data traversal is determined by
			// the data width in case of 8/4/1 bit images
			const uint32_t w = bits_per_pixel >= 24 ? width : width_bytes;
			const uint8_t *line = p_buffer + (line_width * (height - 1));

			for (unsigned int i = 0; i < height; i++) {
				const uint8_t *line_ptr = line;

				for (unsigned int j = 0; j < w; j++) {
					switch (bits_per_pixel) {
						case 1: {
							uint8_t color_index = *line_ptr;

							write_buffer[index + 0] = (color_index >> 7) & 1;
							write_buffer[index + 1] = (color_index >> 6) & 1;
							write_buffer[index + 2] = (color_index >> 5) & 1;
							write_buffer[index + 3] = (color_index >> 4) & 1;
							write_buffer[index + 4] = (color_index >> 3) & 1;
							write_buffer[index + 5] = (color_index >> 2) & 1;
							write_buffer[index + 6] = (color_index >> 1) & 1;
							write_buffer[index + 7] = (color_index >> 0) & 1;

							index += 8;
							line_ptr += 1;
						} break;
						case 4: {
							uint8_t color_index = *line_ptr;

							write_buffer[index + 0] = (color_index >> 4) & 0x0f;
							write_buffer[index + 1] = color_index & 0x0f;

							index += 2;
							line_ptr += 1;
						} break;
						case 8: {
							uint8_t color_index = *line_ptr;

							write_buffer[index] = color_index;

							index += 1;
							line_ptr += 1;
						} break;
						case 24: {
							uint32_t color = *((uint32_t *)line_ptr);

							write_buffer[index + 2] = color & 0xff;
							write_buffer[index + 1] = (color >> 8) & 0xff;
							write_buffer[index + 0] = (color >> 16) & 0xff;
							write_buffer[index + 3] = 0xff;

							index += 4;
							line_ptr += 3;
						} break;
						case 32: {
							uint32_t color = *((uint32_t *)line_ptr);

							write_buffer[index + 2] = color & 0xff;
							write_buffer[index + 1] = (color >> 8) & 0xff;
							write_buffer[index + 0] = (color >> 16) & 0xff;
							write_buffer[index + 3] = color >> 24;

							index += 4;
							line_ptr += 4;
						} break;
					}
				}
				line -= line_width;
			}

			if (p_color_buffer == NULL || color_table_size == 0) { // regular pixels

				p_image->create(width, height, 0, Image::FORMAT_RGBA8, data);

			} else { // data is in indexed format, extend it

				// Palette data
				PoolVector<uint8_t> palette_data;
				palette_data.resize(color_table_size * 4);

				PoolVector<uint8_t>::Write palette_data_w = palette_data.write();
				uint8_t *pal = palette_data_w.ptr();

				const uint8_t *cb = p_color_buffer;

				for (unsigned int i = 0; i < color_table_size; ++i) {
					uint32_t color = *((uint32_t *)cb);

					pal[i * 4 + 0] = (color >> 16) & 0xff;
					pal[i * 4 + 1] = (color >> 8) & 0xff;
					pal[i * 4 + 2] = (color)&0xff;
					pal[i * 4 + 3] = 0xff;

					cb += 4;
				}
				// Extend palette to image
				PoolVector<uint8_t> extended_data;
				extended_data.resize(data.size() * 4);

				PoolVector<uint8_t>::Write ex_w = extended_data.write();
				uint8_t *dest = ex_w.ptr();

				const int num_pixels = width * height;

				for (int i = 0; i < num_pixels; i++) {
					dest[0] = pal[write_buffer[i] * 4 + 0];
					dest[1] = pal[write_buffer[i] * 4 + 1];
					dest[2] = pal[write_buffer[i] * 4 + 2];
					dest[3] = pal[write_buffer[i] * 4 + 3];

					dest += 4;
				}
				p_image->create(width, height, 0, Image::FORMAT_RGBA8, extended_data);
			}
		}
	}
	return err;
}

Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f,
		bool p_force_linear, float p_scale) {

	bmp_header_s bmp_header;
	Error err = ERR_INVALID_DATA;

	// A valid bmp file should always at least have a
	// file header and a minimal info header
	if (f->get_len() > BITMAP_FILE_HEADER_SIZE + BITMAP_INFO_HEADER_MIN_SIZE) {
		// File Header
		bmp_header.bmp_file_header.bmp_signature = f->get_16();
		if (bmp_header.bmp_file_header.bmp_signature == BITMAP_SIGNATURE) {
			bmp_header.bmp_file_header.bmp_file_size = f->get_32();
			bmp_header.bmp_file_header.bmp_file_padding = f->get_32();
			bmp_header.bmp_file_header.bmp_file_offset = f->get_32();

			// Info Header
			bmp_header.bmp_info_header.bmp_header_size = f->get_32();
			ERR_FAIL_COND_V(bmp_header.bmp_info_header.bmp_header_size < BITMAP_INFO_HEADER_MIN_SIZE, ERR_FILE_CORRUPT);

			bmp_header.bmp_info_header.bmp_width = f->get_32();
			bmp_header.bmp_info_header.bmp_height = f->get_32();

			bmp_header.bmp_info_header.bmp_planes = f->get_16();
			ERR_FAIL_COND_V(bmp_header.bmp_info_header.bmp_planes != 1, ERR_FILE_CORRUPT);

			bmp_header.bmp_info_header.bmp_bit_count = f->get_16();
			bmp_header.bmp_info_header.bmp_compression = f->get_32();
			bmp_header.bmp_info_header.bmp_size_image = f->get_32();
			bmp_header.bmp_info_header.bmp_pixels_per_meter_x = f->get_32();
			bmp_header.bmp_info_header.bmp_pixels_per_meter_y = f->get_32();
			bmp_header.bmp_info_header.bmp_colors_used = f->get_32();
			bmp_header.bmp_info_header.bmp_important_colors = f->get_32();

			// Compressed bitmaps not supported, stop parsing
			if (bmp_header.bmp_info_header.bmp_compression != BI_RGB) {
				ERR_EXPLAIN("Unsupported bmp file: " + f->get_path());
				f->close();
				ERR_FAIL_V(ERR_UNAVAILABLE);
			}
			// Don't rely on sizeof(bmp_file_header) as structure padding
			// adds 2 bytes offset leading to misaligned color table reading
			uint32_t ct_offset = BITMAP_FILE_HEADER_SIZE +
								 bmp_header.bmp_info_header.bmp_header_size;
			f->seek(ct_offset);

			uint32_t color_table_size = 0;

			// bmp_colors_used may report 0 despite having a color table
			// for 4 and 1 bit images, so don't rely on this information
			if (bmp_header.bmp_info_header.bmp_bit_count <= 8) {
				// Support 256 colors max
				color_table_size = 1 << bmp_header.bmp_info_header.bmp_bit_count;
				ERR_FAIL_COND_V(color_table_size == 0, ERR_BUG);
			}

			PoolVector<uint8_t> bmp_color_table;
			// Color table is usually 4 bytes per color -> [B][G][R][0]
			bmp_color_table.resize(color_table_size * 4);
			PoolVector<uint8_t>::Write bmp_color_table_w = bmp_color_table.write();
			f->get_buffer(bmp_color_table_w.ptr(), color_table_size * 4);

			f->seek(bmp_header.bmp_file_header.bmp_file_offset);

			uint32_t bmp_buffer_size = (bmp_header.bmp_file_header.bmp_file_size -
										bmp_header.bmp_file_header.bmp_file_offset);

			PoolVector<uint8_t> bmp_buffer;
			err = bmp_buffer.resize(bmp_buffer_size);
			if (err == OK) {
				PoolVector<uint8_t>::Write bmp_buffer_w = bmp_buffer.write();
				f->get_buffer(bmp_buffer_w.ptr(), bmp_buffer_size);

				PoolVector<uint8_t>::Read bmp_buffer_r = bmp_buffer.read();
				PoolVector<uint8_t>::Read bmp_color_table_r = bmp_color_table.read();
				err = convert_to_image(p_image, bmp_buffer_r.ptr(),
						bmp_color_table_r.ptr(), color_table_size, bmp_header);
			}
			f->close();
		}
	}
	return err;
}

void ImageLoaderBMP::get_recognized_extensions(
		List<String> *p_extensions) const {

	p_extensions->push_back("bmp");
}

ImageLoaderBMP::ImageLoaderBMP() {}
<|endoftext|>


<commit_before>#include "sample.hpp"

#include <cstdio>
#include <cstdlib>
#include <cassert>

#include <vector>

#include <signal.h>

#include <GL/glew.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include "shader.hpp"

class FBOSample : public Sample
{
public:
	virtual bool initContents()
	{
		glClearColor(0.0f, 0.0f, 0.3f, 0.0f);

		glGenVertexArrays(1, &_contentVAO);
		assert(_contentVAO != -1);

		glBindVertexArray(_contentVAO);
		{
			_contentProgram = LoadShaders("content.vert", "content.frag");
			assert(_contentProgram != -1);

			static const GLfloat g_vertex_buffer_data[] = {
				-1.0f, -1.0f, 0.0f,
				 1.0f, -1.0f, 0.0f,
				 0.0f,  1.0f, 0.0f,
			};

			glGenBuffers(1, &_contentVBO);
			assert(_contentVBO != -1);

			glBindBuffer(GL_ARRAY_BUFFER, _contentVBO);
			glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

			glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

			GLfloat* transform_buffer_data = new float[16 * 4];
			{
				for (int i = 0; i < 4; ++i) {
					auto T = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, (float)i, 0.0f));
					auto R = glm::rotate(glm::mat4(1.0f), _globalTimer, glm::vec3(0.0f, 1.0f, 0.0f));
					auto S = glm::scale(glm::mat4(1.0f), glm::vec3(1.0f, 1.0f, 1.0f));

					auto M = T * R * S;

					memcpy(transform_buffer_data + 16 * i, glm::value_ptr(M), sizeof(float) * 16);
				}

				glGenBuffers(1, &_contentTransformBO);
				glBindBuffer(GL_TEXTURE_BUFFER, _contentTransformBO);
				glBufferData(GL_TEXTURE_BUFFER, sizeof(float) * 16 * 4,
						transform_buffer_data, GL_STATIC_DRAW);

				glGenTextures(1, &_contentTransformTBO);
				glBindTexture(GL_TEXTURE_BUFFER, _contentTransformTBO);
				glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, _contentTransformBO);
			}
			if (transform_buffer_data) {
				delete[] transform_buffer_data;
				transform_buffer_data = nullptr;
			}
		}
		glBindVertexArray(0);

		_contentVPID = glGetUniformLocation(_contentProgram, "VP");
		_contentMsID = glGetUniformLocation(_contentProgram, "Ms");

		_globalTimer = 0.0f;

		return true;
	}

	virtual void destroyContents()
	{
		glDeleteVertexArrays(1, &_contentVAO);
		glDeleteBuffers(1, &_contentVBO);
		glDeleteBuffers(1, &_contentTransformBO);

		glDeleteTextures(1, &_contentTransformTBO);

		glDeleteProgram(_contentProgram);
	}

	virtual void update(float dt)
	{
		glBindVertexArray(_contentVAO);
		glBindBuffer(GL_TEXTURE_BUFFER, _contentTransformBO);

		float* pointer = (float*)glMapBufferRange(GL_TEXTURE_BUFFER, 0, sizeof(float) * 16 * 4, GL_MAP_WRITE_BIT);
		assert(pointer);

		for (int i = 0; i < 4; ++i) {
			auto T = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, (float)i, 0.0f));
			auto R = glm::rotate(glm::mat4(1.0f), _globalTimer, glm::vec3(0.0f, 1.0f, 0.0f));
			auto S = glm::scale(glm::mat4(1.0f), glm::vec3(1.0f, 1.0f, 1.0f));

			auto M = T * R * S;

			memcpy(pointer + 16 * i, glm::value_ptr(M), sizeof(float) * 16);
		}

		glUnmapBuffer(GL_TEXTURE_BUFFER);

		auto V = glm::lookAt(
				glm::vec3(3,4,10),
				glm::vec3(0,0,0),
				glm::vec3(0,1,0)
				);

		auto P = glm::perspective(45.0f, (float)windowWidth() / windowHeight(),
				0.1f, 100.0f);

		auto VP = P * V;

		glUseProgram(_contentProgram);
		glUniform1i(_contentMsID, 0);
		glUniformMatrix4fv(_contentVPID, 1, false, glm::value_ptr(VP));
		glUseProgram(0);

		_globalTimer += dt;
	}

	virtual void render()
	{
		glViewport(0, 0, windowWidth(), windowHeight());
		glClear(GL_COLOR_BUFFER_BIT);

		glUseProgram(_contentProgram);

		glBindVertexArray(_contentVAO);
		{
			glEnableVertexAttribArray(0);

			glActiveTexture(GL_TEXTURE0);
			glBindTexture(GL_TEXTURE_BUFFER, _contentTransformTBO);
			glDrawArraysInstanced(GL_TRIANGLES, 0, 3, 4);

			glDisableVertexAttribArray(0);
		}

		glUseProgram(0);
	}

private:
	GLuint _contentVAO, _contentVBO;
	GLuint _contentTransformBO;
	GLuint _contentTransformTBO;

	GLuint _contentProgram;
	GLuint _contentVPID;
	GLuint _contentMsID;

	float _globalTimer;
};

Sample* sample = nullptr;

void interruptHandler(int signal)
{
	if (sample == nullptr)
		return;

	sample->destroy();

	if (sample) {
		delete sample;
		sample = nullptr;
	}

	printf("interrupted!\n");

	exit(EXIT_SUCCESS);
}

int main(int argc, char** argv)
{
	if (signal(SIGINT, interruptHandler) == SIG_ERR) {
		fprintf(stderr, "cannot catch signal\n");
	}

	sample = new FBOSample();
	sample->init();

	sample->run();

	sample->destroy();

	if (sample) {
		delete sample;
		sample = nullptr;
	}

	return 0;
}
<commit_msg>Separate prototype and implementation<commit_after>#include "sample.hpp"

#include <cstdio>
#include <cstdlib>
#include <cassert>

#include <vector>

#include <signal.h>

#include <GL/glew.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include "shader.hpp"

class FBOSample : public Sample
{
public:
	virtual bool initContents();
	virtual void destroyContents();
	virtual void update(float dt);
	virtual void render();

private:
	GLuint _contentVAO, _contentVBO;
	GLuint _contentTransformBO;
	GLuint _contentTransformTBO;

	GLuint _contentProgram;
	GLuint _contentVPID;
	GLuint _contentMsID;

	float _globalTimer;

	GLuint _fboVAO, _fboVBO;

	GLuint _fboProgram;
};

Sample* sample = nullptr;

void interruptHandler(int signal)
{
	if (sample == nullptr)
		return;

	sample->destroy();

	if (sample) {
		delete sample;
		sample = nullptr;
	}

	printf("interrupted!\n");

	exit(EXIT_SUCCESS);
}

int main(int argc, char** argv)
{
	if (signal(SIGINT, interruptHandler) == SIG_ERR) {
		fprintf(stderr, "cannot catch signal\n");
	}

	sample = new FBOSample();
	sample->init();

	sample->run();

	sample->destroy();

	if (sample) {
		delete sample;
		sample = nullptr;
	}

	return 0;
}

bool FBOSample::initContents()
{
	glClearColor(0.0f, 0.0f, 0.3f, 0.0f);

	// Init Content
	glGenVertexArrays(1, &_contentVAO);
	assert(_contentVAO != -1);

	glBindVertexArray(_contentVAO);
	{
		_contentProgram = LoadShaders("content.vert", "content.frag");
		assert(_contentProgram != -1);

		static const GLfloat g_vertex_buffer_data[] = {
			-1.0f, -1.0f, 0.0f,
			 1.0f, -1.0f, 0.0f,
			 0.0f,  1.0f, 0.0f,
		};

		glGenBuffers(1, &_contentVBO);
		assert(_contentVBO != -1);

		glBindBuffer(GL_ARRAY_BUFFER, _contentVBO);
		glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

		GLfloat* transform_buffer_data = new float[16 * 4];
		{
			for (int i = 0; i < 4; ++i) {
				auto T = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, (float)i, 0.0f));
				auto R = glm::rotate(glm::mat4(1.0f), _globalTimer, glm::vec3(0.0f, 1.0f, 0.0f));
				auto S = glm::scale(glm::mat4(1.0f), glm::vec3(1.0f, 1.0f, 1.0f));

				auto M = T * R * S;

				memcpy(transform_buffer_data + 16 * i, glm::value_ptr(M), sizeof(float) * 16);
			}

			glGenBuffers(1, &_contentTransformBO);
			glBindBuffer(GL_TEXTURE_BUFFER, _contentTransformBO);
			glBufferData(GL_TEXTURE_BUFFER, sizeof(float) * 16 * 4,
					transform_buffer_data, GL_STATIC_DRAW);

			glGenTextures(1, &_contentTransformTBO);
			glBindTexture(GL_TEXTURE_BUFFER, _contentTransformTBO);
			glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, _contentTransformBO);
		}
		if (transform_buffer_data) {
			delete[] transform_buffer_data;
			transform_buffer_data = nullptr;
		}
	}
	glBindVertexArray(0);

	_contentVPID = glGetUniformLocation(_contentProgram, "VP");
	_contentMsID = glGetUniformLocation(_contentProgram, "Ms");

	_globalTimer = 0.0f;

	return true;
}

void FBOSample::destroyContents()
{
	glDeleteVertexArrays(1, &_contentVAO);
	glDeleteBuffers(1, &_contentVBO);
	glDeleteBuffers(1, &_contentTransformBO);

	glDeleteTextures(1, &_contentTransformTBO);

	glDeleteProgram(_contentProgram);
}

void FBOSample::update(float dt)
{
	glBindVertexArray(_contentVAO);
	glBindBuffer(GL_TEXTURE_BUFFER, _contentTransformBO);

	float* pointer = (float*)glMapBufferRange(GL_TEXTURE_BUFFER, 0, sizeof(float) * 16 * 4, GL_MAP_WRITE_BIT);
	assert(pointer);

	for (int i = 0; i < 4; ++i) {
		auto T = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, (float)i, 0.0f));
		auto R = glm::rotate(glm::mat4(1.0f), _globalTimer, glm::vec3(0.0f, 1.0f, 0.0f));
		auto S = glm::scale(glm::mat4(1.0f), glm::vec3(1.0f, 1.0f, 1.0f));

		auto M = T * R * S;

		memcpy(pointer + 16 * i, glm::value_ptr(M), sizeof(float) * 16);
	}

	glUnmapBuffer(GL_TEXTURE_BUFFER);

	auto V = glm::lookAt(
			glm::vec3(3,4,10),
			glm::vec3(0,0,0),
			glm::vec3(0,1,0)
			);

	auto P = glm::perspective(45.0f, (float)windowWidth() / windowHeight(),
			0.1f, 100.0f);

	auto VP = P * V;

	glUseProgram(_contentProgram);
	glUniform1i(_contentMsID, 0);
	glUniformMatrix4fv(_contentVPID, 1, false, glm::value_ptr(VP));
	glUseProgram(0);

	_globalTimer += dt;
}

void FBOSample::render()
{
	glViewport(0, 0, windowWidth(), windowHeight());
	glClear(GL_COLOR_BUFFER_BIT);

	glUseProgram(_contentProgram);

	glBindVertexArray(_contentVAO);
	{
		glEnableVertexAttribArray(0);

		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_BUFFER, _contentTransformTBO);
		glDrawArraysInstanced(GL_TRIANGLES, 0, 3, 4);

		glDisableVertexAttribArray(0);
	}

	glUseProgram(0);
}

<|endoftext|>