base: Redesign internal frame buffer handling

Currently, frame buffer handling in gem5 is quite ad hoc. In practice,
we pass around naked pointers to raw pixel data and expect consumers
to convert frame buffers using the (broken) VideoConverter.

This changeset completely redesigns the way we handle frame buffers
internally. In summary, it fixes several color conversion bugs, adds
support for more color formats (e.g., big endian), and makes the code
base easier to follow.

In the new world, gem5 always represents pixel data using the Pixel
struct when pixels need to be passed between different classes (e.g.,
a display controller and the VNC server). Producers of entire frames
(e.g., display controllers) should use the FrameBuffer class to
represent a frame.

Frame producers are expected to create one instance of the FrameBuffer
class in their constructors and register it with its consumers
once. Consumers are expected to check the dimensions of the frame
buffer when they consume it.

Conversion between the external representation and the internal
representation is supported for all common "true color" RGB formats of
up to 32-bit color depth. The external pixel representation is
expected to be between 1 and 4 bytes in either big endian or little
endian. Color channels are assumed to be contiguous ranges of bits
within each pixel word. The external pixel value is scaled to an 8-bit
internal representation using a floating multiplication to map it to
the entire 8-bit range.
This commit is contained in:
Andreas Sandberg 2015-05-23 13:37:03 +01:00
parent 1985d28ef9
commit db5c9a5f90
20 changed files with 925 additions and 633 deletions

View file

@ -42,6 +42,7 @@ Source('cprintf.cc')
Source('debug.cc')
if env['USE_FENV']:
Source('fenv.c')
Source('framebuffer.cc')
Source('hostinfo.cc')
Source('inet.cc')
Source('inifile.cc')

View file

@ -47,11 +47,8 @@
#include "base/misc.hh"
// bitmap class ctor
Bitmap::Bitmap(VideoConvert::Mode mode, uint16_t w, uint16_t h, uint8_t *d)
: height(h), width(w),
header(getCompleteHeader()),
data(d),
vc(mode, VideoConvert::rgb8888, width, height)
Bitmap::Bitmap(const FrameBuffer *_fb)
: fb(*_fb)
{
}
@ -62,7 +59,7 @@ Bitmap::~Bitmap()
const Bitmap::CompleteV1Header
Bitmap::getCompleteHeader() const
{
const uint32_t pixel_array_size(sizeof(PixelType) * width * height);
const uint32_t pixel_array_size(sizeof(PixelType) * fb.area());
const uint32_t file_size(sizeof(CompleteV1Header) + pixel_array_size);
const CompleteV1Header header = {
@ -76,8 +73,8 @@ Bitmap::getCompleteHeader() const
// Info/DIB header
{
sizeof(InfoHeaderV1),
width,
height,
fb.width(),
fb.height(),
1, /* Color planes */
32, /* Bits per pixel */
0, /* No compression */
@ -93,28 +90,25 @@ Bitmap::getCompleteHeader() const
}
void
Bitmap::write(std::ostream *bmp) const
Bitmap::write(std::ostream &bmp) const
{
assert(data);
const CompleteV1Header header(getCompleteHeader());
// 1. write the header
bmp->write(reinterpret_cast<const char *>(&header), sizeof(header));
bmp.write(reinterpret_cast<const char *>(&header), sizeof(header));
// 2. write the bitmap data
const uint8_t *pixels(vc.convert(data));
// BMP start store data left to right starting with the bottom row
// so we need to do some creative flipping
for (int y = height - 1; y >= 0; y--) {
for (int x = 0; x < width; x++) {
bmp->write(
(const char *)&pixels[sizeof(PixelType) * (y * width + x)],
sizeof(PixelType));
}
std::vector<PixelType> line_buffer(fb.width());
for (int y = 0; y < fb.height(); ++y) {
for (unsigned x = 0; x < fb.width(); ++x)
line_buffer[x] = fb.pixel(x, fb.height() - y - 1);
bmp.write(reinterpret_cast<const char *>(line_buffer.data()),
line_buffer.size() * sizeof(line_buffer[0]));
}
bmp->flush();
delete[] pixels;
bmp.flush();
}

View file

@ -45,7 +45,7 @@
#include <ostream>
#include "base/compiler.hh"
#include "base/vnc/convert.hh"
#include "base/framebuffer.hh"
/**
* @file Declaration of a class that writes a frame buffer to a bitmap
@ -59,38 +59,17 @@ class Bitmap
/**
* Create a bitmap that takes data in a given mode & size and
* outputs to an ostream.
*
* @param mode the type of data that is being provided
* @param h the hight of the image
* @param w the width of the image
* @param d the data for the image in mode
*/
Bitmap(VideoConvert::Mode mode, uint16_t w, uint16_t h, uint8_t *d);
Bitmap(const FrameBuffer *fb);
~Bitmap();
/**
* Provide the converter with the data that should be output. It
* will be converted into rgb8888 and written when write() is
* called.
*
* @param d the data
*/
void rawData(uint8_t* d) { data = d; }
/**
* Write the frame buffer data into the provided ostream
*
* @param bmp stream to write to
*/
void write(std::ostream *bmp) const;
/**
* Gets a hash over the bitmap for quick comparisons to other bitmaps.
*
* @return hash of the bitmap
*/
uint64_t getHash() const { return vc.getHash(data); }
void write(std::ostream &bmp) const;
private:
@ -121,16 +100,26 @@ class Bitmap
InfoHeaderV1 info;
} M5_ATTR_PACKED;
typedef uint32_t PixelType;
struct BmpPixel32 {
BmpPixel32 &operator=(const Pixel &rhs) {
red = rhs.red;
green = rhs.green;
blue = rhs.blue;
padding = 0;
return *this;
}
uint8_t blue;
uint8_t green;
uint8_t red;
uint8_t padding;
} M5_ATTR_PACKED;
typedef BmpPixel32 PixelType;
const CompleteV1Header getCompleteHeader() const;
const uint16_t height;
const uint16_t width;
const CompleteV1Header header;
uint8_t *data;
VideoConvert vc;
const FrameBuffer &fb;
};
#endif // __BASE_BITMAP_HH__

173
src/base/framebuffer.cc Normal file
View file

@ -0,0 +1,173 @@
/*
* Copyright (c) 2015 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors: Andreas Sandberg
*/
#include "base/framebuffer.hh"
#include <zlib.h>
#include <cassert>
#include "base/bitfield.hh"
const PixelConverter PixelConverter::rgba8888_le(4, 0, 8, 16, 8, 8, 8);
const PixelConverter PixelConverter::rgba8888_be(4, 0, 8, 16, 8, 8, 8,
BigEndianByteOrder);
const PixelConverter PixelConverter::rgb565_le(2, 0, 5, 11, 5, 6, 5);
const PixelConverter PixelConverter::rgb565_be(2, 0, 5, 11, 5, 6, 5,
BigEndianByteOrder);
const FrameBuffer FrameBuffer::dummy(320, 240);
PixelConverter::PixelConverter(unsigned _length,
unsigned ro, unsigned go, unsigned bo,
unsigned rw, unsigned gw, unsigned bw,
ByteOrder _byte_order)
: length(_length),
depth(rw + gw + bw),
byte_order(_byte_order),
ch_r(ro, rw),
ch_g(go, gw),
ch_b(bo, bw)
{
assert(length > 1);
}
PixelConverter::Channel::Channel(unsigned _offset, unsigned width)
: offset(_offset),
mask(::mask(width)),
factor(255.0 / mask)
{
}
uint32_t
PixelConverter::readWord(const uint8_t *p) const
{
uint32_t word(0);
if (byte_order == LittleEndianByteOrder) {
for (int i = 0; i < length; ++i)
word |= p[i] << (8 * i);
} else {
for (int i = 0; i < length; ++i)
word |= p[i] << (8 * (length - i - 1));
}
return word;
}
void
PixelConverter::writeWord(uint8_t *p, uint32_t word) const
{
if (byte_order == LittleEndianByteOrder) {
for (int i = 0; i < length; ++i)
p[i] = (word >> (8 * i)) & 0xFF;
} else {
for (int i = 0; i < length; ++i)
p[i] = (word >> (8 * (length - i - 1))) & 0xFF;
}
}
FrameBuffer::FrameBuffer(unsigned width, unsigned height)
: pixels(width * height),
_width(width), _height(height)
{
clear();
}
FrameBuffer::FrameBuffer()
: _width(0), _height(0)
{
}
FrameBuffer::~FrameBuffer()
{
}
void
FrameBuffer::resize(unsigned width, unsigned height)
{
_width = width;
_height = height;
pixels.resize(width * height);
}
void
FrameBuffer::fill(const Pixel &pixel)
{
for (auto &p : pixels)
p = pixel;
}
void
FrameBuffer::clear()
{
static const Pixel black(0, 0, 0);
fill(black);
}
void
FrameBuffer::copyIn(const uint8_t *fb, const PixelConverter &conv)
{
for (auto &p : pixels) {
p = conv.toPixel(fb);
fb += conv.length;
}
}
void
FrameBuffer::copyOut(uint8_t *fb, const PixelConverter &conv) const
{
for (auto &p : pixels) {
conv.fromPixel(fb, p);
fb += conv.length;
}
}
uint64_t
FrameBuffer::getHash() const
{
return adler32(0UL,
reinterpret_cast<const Bytef *>(pixels.data()),
area() * sizeof(Pixel));
}

355
src/base/framebuffer.hh Normal file
View file

@ -0,0 +1,355 @@
/*
* Copyright (c) 2015 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors: Andreas Sandberg
*/
#ifndef __BASE_FRAMEBUFFER_HH__
#define __BASE_FRAMEBUFFER_HH__
#include <cmath>
#include <cstdint>
#include <vector>
#include "base/types.hh"
/**
* Internal gem5 representation of a Pixel.
*/
struct Pixel
{
Pixel()
: red(0), green(0), blue(0), padding(0) {}
Pixel(uint8_t _red, uint8_t _green, uint8_t _blue)
: red(_red), green(_green), blue(_blue), padding(0) {}
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t padding;
};
inline bool
operator==(const Pixel &lhs, const Pixel &rhs)
{
return lhs.red == rhs.red &&
lhs.green == rhs.green &&
lhs.blue == rhs.blue &&
lhs.padding == rhs.padding;
}
/**
* Configurable RGB pixel converter.
*
* This class converts between external RGB representations and gem5's
* internal Pixel representation. The class assumes that pixels are
* stored in a word of configurable length (up to 32 bits). Individual
* pixels are assumed to be represented by contiguous bit ranges in
* the word (i.e., it is possible to shift and mask out a contiguous
* bit range for each pixel).
*/
class PixelConverter
{
public:
/**
* Color channel conversion and scaling helper class.
*/
struct Channel {
/**
* @param offset Offset in bits.
* @param width Width in bits.
*/
Channel(unsigned offset, unsigned width);
/**
* Get the value of a single color channel represented as an
* 8-bit number.
*/
uint8_t toPixel(uint32_t word) const {
return round(((word >> offset) & mask) * factor);
}
/**
* Convert an 8-bit representation of a color into an external
* format.
*/
uint32_t fromPixel(uint8_t ch) const {
return (static_cast<uint8_t>(round(ch / factor)) & mask) << offset;
}
/** Offset in bits */
unsigned offset;
/** Bit mask (after shifting) */
unsigned mask;
/**
* Scaling factor when converting to the full range of an
* 8-bit color channel
*/
float factor;
};
PixelConverter(unsigned length,
unsigned ro, unsigned go, unsigned bo,
unsigned rw, unsigned gw, unsigned bw,
ByteOrder byte_order = LittleEndianByteOrder);
/** Get the Pixel representation of a color word. */
Pixel toPixel(uint32_t word) const {
return Pixel(ch_r.toPixel(word),
ch_g.toPixel(word),
ch_b.toPixel(word));
}
/** Get a Pixel representation by reading a word from memory. */
Pixel toPixel(const uint8_t *rfb) const {
return toPixel(readWord(rfb));
}
/** Convert a Pixel into a color word */
uint32_t fromPixel(const Pixel &pixel) const {
return ch_r.fromPixel(pixel.red) |
ch_g.fromPixel(pixel.green) |
ch_b.fromPixel(pixel.blue);
}
/**
* Convert a pixel into a color word and store the resulting word
* in memory.
*/
void fromPixel(uint8_t *rfb, const Pixel &pixel) const {
writeWord(rfb, fromPixel(pixel));
}
/**
* Read a word of a given length and endianness from memory.
*
* The number of bytes read from memory is determined by the
* length of a color word. Note that some of the bytes may be
* padding.
*
* @param p Pointer to the first byte in the word.
* @return Word in host endianness.
*/
uint32_t readWord(const uint8_t *p) const;
/**
* Write a word of a given length and endianness to memory.
*
* @param p Pointer to the first byte in memory.
* @param word Word to store (host endianness).
*/
void writeWord(uint8_t *p, uint32_t word) const;
/** Bytes per pixel when stored in memory (including padding) */
unsigned length;
/**
* Number of bits used to represent one pixel value (excluding
* padding). This could be less than length * 8 if the pixel value
* is padded.
*/
unsigned depth;
/** Byte order when stored to memory. */
ByteOrder byte_order;
/** Red channel conversion helper */
Channel ch_r;
/** Green channel conversion helper */
Channel ch_g;
/** Blue channel conversion helper */
Channel ch_b;
/** Predefined 32-bit RGB (red in least significant bits, 8
* bits/channel, little endian) conversion helper */
static const PixelConverter rgba8888_le;
/** Predefined 16-bit RGB565 (red in least significant bits,
* little endian) conversion helper */
static const PixelConverter rgb565_le;
/** Predefined 32-bit RGB (red in least significant bits, 8
* bits/channel, big endian) conversion helper */
static const PixelConverter rgba8888_be;
/** Predefined 16-bit RGB565 (red in least significant bits,
* big endian) conversion helper */
static const PixelConverter rgb565_be;
};
/**
* Internal gem5 representation of a frame buffer
*
* Display controllers and other devices producing images are expected
* to use this class to represent the final image.
*
* Pixels are indexed relative to the upper left corner of the
* image. That is, the pixel at position (0, 0) is the upper left
* corner. The backing store is a linear vector of Pixels ordered left
* to right starting in the upper left corner.
*/
class FrameBuffer
{
public:
/**
* Create a frame buffer of a given size.
*
* @param width Width in pixels
* @param height Height in pixels
*/
FrameBuffer(unsigned width, unsigned height);
/** Create an empty (0x0) frame buffer */
FrameBuffer();
virtual ~FrameBuffer();
/**
* Resize the frame buffer.
*
* This method resizes frame buffer including the backing
* store. The contents of the backing store are undefined after
* this operation.
*
* @param with Width in pixels.
* @param height Height in pixels.
*/
void resize(unsigned width, unsigned height);
/** Frame buffer width in pixels */
unsigned width() const { return _width; }
/** Frame buffer height in pixels */
unsigned height() const { return _height; }
/** Total number of pixels in frame buffer */
unsigned area() const { return _width * _height; }
/**
* Fill the frame buffer with a single pixel value
*
* @param pixel Pixel value to fill with.
*/
void fill(const Pixel &pixel);
/**
* Fill the frame buffer with black pixels
*/
void clear();
/**
* Fill the frame buffer with pixel data from an external buffer
* of the same width and height as this frame buffer.
*
* @param fb External frame buffer
* @param conv Pixel conversion helper
*/
void copyIn(const uint8_t *fb, const PixelConverter &conv);
/**
* Fill the frame buffer with pixel data from an external buffer
* of the same width and height as this frame buffer.
*
* @param fb External frame buffer
* @param conv Pixel conversion helper
*/
void copyIn(const std::vector<uint8_t> &fb, const PixelConverter &conv) {
copyIn(fb.data(), conv);
}
/**
* Store the contents of this frame buffer in an external buffer
* of the same width and height as this frame buffer.
*
* @param fb External frame buffer
* @param conv Pixel conversion helper
*/
void copyOut(uint8_t *fb, const PixelConverter &conv) const;
/**
* Store the contents of this frame buffer in an external buffer
* of the same width and height as this frame buffer.
*
* @param fb External frame buffer
* @param conv Pixel conversion helper
*/
void copyOut(std::vector<uint8_t> &fb, const PixelConverter &conv) const {
copyOut(fb.data(), conv);
}
/**
* Get a pixel from an (x, y) coordinate
*
* @param x Distance from the left margin.
* @param y Distance from the top of the frame.
*/
const Pixel &pixel(unsigned x, unsigned y) const {
assert(x < _width);
assert(y < _height);
return pixels[y * _width + x];
}
/**
* Get a pixel from an (x, y) coordinate
*
* @param x Distance from the left margin.
* @param y Distance from the top of the frame.
*/
Pixel &pixel(unsigned x, unsigned y) {
assert(x < _width);
assert(y < _height);
return pixels[y * _width + x];
}
/**
* Create a hash of the image that can be used for quick
* comparisons.
*/
uint64_t getHash() const;
/**
* Static "dummy" frame buffer.
*
* This is a dummy frame buffer that can be used as a place holder
* for devices that always expect a frame buffer to be present.
*/
static const FrameBuffer dummy;
/** Frame buffer backing store */
std::vector<Pixel> pixels;
protected:
/** Width in pixels */
unsigned _width;
/** Height in pixels */
unsigned _height;
};
#endif // __BASE_FRAMEBUFFER_HH__

View file

@ -188,4 +188,9 @@ typedef std::shared_ptr<FaultBase> Fault;
constexpr decltype(nullptr) NoFault = nullptr;
#endif
enum ByteOrder {
BigEndianByteOrder,
LittleEndianByteOrder
};
#endif // __BASE_TYPES_HH__

View file

@ -39,8 +39,6 @@
Import('*')
Source('convert.cc')
SimObject('Vnc.py')
Source('vncinput.cc')
Source('vncserver.cc')

View file

@ -1,183 +0,0 @@
/*
* Copyright (c) 2011 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors: Ali Saidi
* William Wang
*/
#include <cassert>
#include "base/vnc/convert.hh"
#include "base/misc.hh"
/** @file
* This file provides conversion functions for a variety of video modes
*/
VideoConvert::VideoConvert(Mode input_mode, Mode output_mode, int _width,
int _height)
: inputMode(input_mode), outputMode(output_mode), width(_width),
height(_height)
{
if (inputMode != bgr565 && inputMode != rgb565 &&
inputMode != bgr8888 && inputMode != bgr888)
fatal("Only support converting from bgr565, rdb565, "
"bgr8888 and bgr888\n");
if (outputMode != rgb8888)
fatal("Only support converting to rgb8888\n");
assert(0 < height && height < 4000);
assert(0 < width && width < 4000);
}
VideoConvert::~VideoConvert()
{
}
uint8_t*
VideoConvert::convert(const uint8_t *fb) const
{
switch (inputMode) {
case bgr565:
return m565rgb8888(fb, true);
case rgb565:
return m565rgb8888(fb, false);
case bgr8888:
return bgr8888rgb8888(fb);
case bgr888:
return bgr888rgb8888(fb);
default:
panic("Unimplemented Mode\n");
}
}
uint8_t*
VideoConvert::m565rgb8888(const uint8_t *fb, bool bgr) const
{
uint8_t *out = new uint8_t[area() * sizeof(uint32_t)];
uint32_t *out32 = (uint32_t*)out;
uint16_t *in16 = (uint16_t*)fb;
for (int x = 0; x < area(); x++) {
Bgr565 inpx;
Rgb8888 outpx = 0;
inpx = in16[x];
if (bgr) {
outpx.red = inpx.blue << 3;
outpx.green = inpx.green << 2;
outpx.blue = inpx.red << 3;
} else {
outpx.blue = inpx.blue << 3;
outpx.green = inpx.green << 2;
outpx.red = inpx.red << 3;
}
out32[x] = outpx;
}
return out;
}
uint8_t*
VideoConvert::bgr8888rgb8888(const uint8_t *fb) const
{
uint8_t *out = new uint8_t[area() * sizeof(uint32_t)];
uint32_t *out32 = (uint32_t*)out;
uint32_t *in32 = (uint32_t*)fb;
for (int x = 0; x < area(); x++) {
Rgb8888 outpx = 0;
Bgr8888 inpx;
inpx = in32[x];
outpx.red = inpx.blue;
outpx.green = inpx.green;
outpx.blue = inpx.red;
out32[x] = outpx;
}
return out;
}
uint8_t*
VideoConvert::bgr888rgb8888(const uint8_t *fb) const
{
uint8_t *out = new uint8_t[area() * sizeof(uint32_t)];
uint32_t *out32 = (uint32_t*)out;
typedef uint8_t In24[3];
const In24 *in24 = (In24 *)fb;
for (int x = 0; x < area(); x++) {
Rgb8888 outpx = 0;
outpx.blue = in24[x][0];
outpx.green = in24[x][1];
outpx.red = in24[x][2];
outpx.alpha = 0xFF;
out32[x] = outpx;
}
return out;
}
/*
uint64_t
VideoConvert::getHash(const uint8_t *fb) const
{
const uint8_t *fb_e = fb + area();
uint64_t hash = 1;
while (fb < fb_e - 8) {
hash += *((const uint64_t*)fb);
fb += 8;
}
while (fb < fb_e) {
hash += *(fb++);
}
return hash;
}*/

View file

@ -1,158 +0,0 @@
/*
* Copyright (c) 2011 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors: Ali Saidi
*/
/** @file
* This file provides conversion functions for a variety of video modes
*/
#ifndef __BASE_VNC_CONVERT_HH__
#define __BASE_VNC_CONVERT_HH__
#include <zlib.h>
#include "base/bitunion.hh"
class VideoConvert
{
public:
enum Mode {
UnknownMode,
bgr565,
rgb565,
bgr8888,
rgb8888,
rgb888,
bgr888,
bgr444,
bgr4444,
rgb444,
rgb4444
};
// supports bpp32 RGB (bmp) and bpp16 5:6:5 mode BGR (linux)
BitUnion32(Rgb8888)
Bitfield<7,0> blue;
Bitfield<15,8> green;
Bitfield<23,16> red;
Bitfield<31,24> alpha;
EndBitUnion(Rgb8888)
BitUnion32(Bgr8888)
Bitfield<7,0> red;
Bitfield<15,8> green;
Bitfield<23,16> blue;
Bitfield<31,24> alpha;
EndBitUnion(Bgr8888)
BitUnion16(Bgr565)
Bitfield<4,0> red;
Bitfield<10,5> green;
Bitfield<15,11> blue;
EndBitUnion(Bgr565)
BitUnion16(Rgb565)
Bitfield<4,0> red;
Bitfield<10,5> green;
Bitfield<15,11> blue;
EndBitUnion(Rgb565)
/** Setup the converter with the given parameters
* @param input_mode type of data that will be provided
* @param output_mode type of data that should be output
* @param _width width of the frame buffer
* @param _height height of the frame buffer
*/
VideoConvert(Mode input_mode, Mode output_mode, int _width, int _height);
/** Destructor
*/
~VideoConvert();
/** Convert the provided frame buffer data into the format specified in the
* constructor.
* @param fb the frame buffer to convert
* @return the converted data (user must free)
*/
uint8_t* convert(const uint8_t *fb) const;
/** Return the number of pixels that this buffer specifies
* @return number of pixels
*/
int area() const { return width * height; }
/**
* Returns a hash on the raw data.
*
* @return hash of the buffer
*/
inline uint64_t getHash(const uint8_t *fb) const {
return adler32(0UL, fb, width * height);
}
private:
/**
* Convert a bgr8888 input to rgb8888.
* @param fb the data to convert
* @return converted data
*/
uint8_t* bgr8888rgb8888(const uint8_t *fb) const;
/**
* Convert a bgr888 input to rgb8888.
* @param fb the data to convert
* @return converted data
*/
uint8_t* bgr888rgb8888(const uint8_t *fb) const;
/**
* Convert a bgr565 or rgb565 input to rgb8888.
* @param fb the data to convert
* @param bgr true if the input data is bgr565
* @return converted data
*/
uint8_t* m565rgb8888(const uint8_t *fb, bool bgr) const;
Mode inputMode;
Mode outputMode;
int width;
int height;
};
#endif // __BASE_VNC_CONVERT_HH__

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010 ARM Limited
* Copyright (c) 2010, 2015 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -53,9 +53,10 @@ using namespace std;
VncInput::VncInput(const Params *p)
: SimObject(p), keyboard(NULL), mouse(NULL),
vc(NULL), fbPtr(NULL), videoMode(VideoConvert::UnknownMode),
_videoWidth(1), _videoHeight(1), captureEnabled(p->frame_capture),
captureCurrentFrame(0), captureLastHash(0), captureBitmap(0)
fb(&FrameBuffer::dummy),
_videoWidth(fb->width()), _videoHeight(fb->height()),
captureEnabled(p->frame_capture),
captureCurrentFrame(0), captureLastHash(0)
{
if (captureEnabled) {
// remove existing frame output directory if it exists, then create a
@ -68,33 +69,40 @@ VncInput::VncInput(const Params *p)
}
void
VncInput::setFrameBufferParams(VideoConvert::Mode mode, uint16_t width,
uint16_t height)
VncInput::setFrameBuffer(const FrameBuffer *rfb)
{
DPRINTF(VNC, "Updating video params: mode: %d width: %d height: %d\n", mode,
width, height);
if (!rfb)
panic("Trying to VNC frame buffer to NULL!");
fb = rfb;
// create bitmap of the frame with new attributes
if (captureEnabled)
captureBitmap.reset(new Bitmap(rfb));
// Setting a new frame buffer means that we need to send an update
// to the client. Mark the internal buffers as dirty to do so.
setDirty();
}
void
VncInput::setDirty()
{
const unsigned width(fb->width());
const unsigned height(fb->height());
if (_videoWidth != width || _videoHeight != height) {
DPRINTF(VNC, "Updating video params: width: %d height: %d\n",
width, height);
if (mode != videoMode || width != videoWidth() || height != videoHeight()) {
videoMode = mode;
_videoWidth = width;
_videoHeight = height;
if (vc)
delete vc;
vc = new VideoConvert(mode, VideoConvert::rgb8888, videoWidth(),
videoHeight());
if (captureEnabled) {
// create bitmap of the frame with new attributes
if (captureBitmap)
delete captureBitmap;
assert(fbPtr);
captureBitmap = new Bitmap(videoMode, width, height, fbPtr);
assert(captureBitmap);
}
frameBufferResized();
}
if (captureEnabled)
captureFrameBuffer();
}
void
@ -103,7 +111,7 @@ VncInput::captureFrameBuffer()
assert(captureBitmap);
// skip identical frames
uint64_t new_hash = captureBitmap->getHash();
uint64_t new_hash = fb->getHash();
if (captureLastHash == new_hash)
return;
captureLastHash = new_hash;
@ -116,8 +124,8 @@ VncInput::captureFrameBuffer()
// create the compressed framebuffer file
ostream *fb_out = simout.create(captureOutputDirectory + frameFilename,
true);
captureBitmap->write(fb_out);
true);
captureBitmap->write(*fb_out);
simout.close(fb_out);
++captureCurrentFrame;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010 ARM Limited
* Copyright (c) 2010, 2015 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -46,8 +46,8 @@
#define __BASE_VNC_VNC_INPUT_HH__
#include <iostream>
#include <memory>
#include "base/vnc/convert.hh"
#include "base/bitmap.hh"
#include "params/VncInput.hh"
#include "sim/sim_object.hh"
@ -160,11 +160,7 @@ class VncInput : public SimObject
* tell us where the data is instead of constanly copying it around
* @param rfb frame buffer that we're going to use
*/
void
setFramebufferAddr(uint8_t* rfb)
{
fbPtr = rfb;
}
virtual void setFrameBuffer(const FrameBuffer *rfb);
/** Set up the device that would like to receive notifications when keys are
* pressed in the vnc client keyboard
@ -196,32 +192,19 @@ class VncInput : public SimObject
* the frame buffer has been updated and a new image needs to be sent to the
* client
*/
virtual void setDirty()
{
if (captureEnabled)
captureFrameBuffer();
}
/** Set the mode of the data the frame buffer will be sending us
* @param mode the mode
*/
virtual void setFrameBufferParams(VideoConvert::Mode mode, uint16_t width, uint16_t height);
virtual void setDirty();
protected:
virtual void frameBufferResized() {};
/** The device to notify when we get key events */
VncKeyboard *keyboard;
/** The device to notify when we get mouse events */
VncMouse *mouse;
/** The video converter that transforms data for us */
VideoConvert *vc;
/** pointer to the actual data that is stored in the frame buffer device */
uint8_t* fbPtr;
/** The mode of data we're getting frame buffer in */
VideoConvert::Mode videoMode;
const FrameBuffer *fb;
/** the width of the frame buffer we are sending to the client */
uint16_t _videoWidth;
@ -242,7 +225,7 @@ class VncInput : public SimObject
uint64_t captureLastHash;
/** Cached bitmap object for writing out frame buffers to file */
Bitmap *captureBitmap;
std::unique_ptr<Bitmap> captureBitmap;
/** Captures the current frame buffer to a file */
void captureFrameBuffer();

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010 ARM Limited
* Copyright (c) 2010, 2015 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -61,6 +61,7 @@
#include <cerrno>
#include <cstdio>
#include <cstddef>
#include "base/atomicio.hh"
#include "base/bitmap.hh"
@ -74,6 +75,12 @@
using namespace std;
const PixelConverter VncServer::pixelConverter(
4, // 4 bytes / pixel
16, 8, 0, // R in [23, 16], G in [15, 8], B in [7, 0]
8, 8, 8, // 8 bits / channel
LittleEndianByteOrder);
/** @file
* Implementiation of a VNC server
*/
@ -122,20 +129,19 @@ VncServer::VncServer(const Params *p)
curState = WaitForProtocolVersion;
// currently we only support this one pixel format
// unpacked 32bit rgb (rgb888 + 8 bits of nothing/alpha)
// keep it around for telling the client and making
// sure the client cooperates
pixelFormat.bpp = 32;
pixelFormat.depth = 24;
pixelFormat.bigendian = 0;
// We currently only support one pixel format. Extract the pixel
// representation from our PixelConverter instance and keep it
// around for telling the client and making sure it cooperates
pixelFormat.bpp = 8 * pixelConverter.length;
pixelFormat.depth = pixelConverter.depth;
pixelFormat.bigendian = pixelConverter.byte_order == BigEndianByteOrder;
pixelFormat.truecolor = 1;
pixelFormat.redmax = 0xff;
pixelFormat.greenmax = 0xff;
pixelFormat.bluemax = 0xff;
pixelFormat.redshift = 16;
pixelFormat.greenshift = 8;
pixelFormat.blueshift = 0;
pixelFormat.redmax = pixelConverter.ch_r.mask;
pixelFormat.greenmax = pixelConverter.ch_g.mask;
pixelFormat.bluemax = pixelConverter.ch_b.mask;
pixelFormat.redshift = pixelConverter.ch_r.offset;
pixelFormat.greenshift = pixelConverter.ch_g.offset;
pixelFormat.blueshift = pixelConverter.ch_b.offset;
DPRINTF(VNC, "Vnc server created at port %d\n", p->port);
}
@ -615,13 +621,11 @@ void
VncServer::sendFrameBufferUpdate()
{
if (!fbPtr || dataFd <= 0 || curState != NormalPhase || !sendUpdate) {
if (dataFd <= 0 || curState != NormalPhase || !sendUpdate) {
DPRINTF(VNC, "NOT sending framebuffer update\n");
return;
}
assert(vc);
// The client will request data constantly, unless we throttle it
sendUpdate = false;
@ -650,19 +654,25 @@ VncServer::sendFrameBufferUpdate()
write(&fbu);
write(&fbr);
assert(fbPtr);
assert(fb);
uint8_t *tmp = vc->convert(fbPtr);
uint64_t num_pixels = videoWidth() * videoHeight();
write(tmp, num_pixels * sizeof(uint32_t));
delete [] tmp;
std::vector<uint8_t> line_buffer(pixelConverter.length * fb->width());
for (int y = 0; y < fb->height(); ++y) {
// Convert and send a line at a time
uint8_t *raw_pixel(line_buffer.data());
for (unsigned x = 0; x < fb->width(); ++x) {
pixelConverter.fromPixel(raw_pixel, fb->pixel(x, y));
raw_pixel += pixelConverter.length;
}
write(line_buffer.data(), line_buffer.size());
}
}
void
VncServer::sendFrameBufferResized()
{
assert(fbPtr && dataFd > 0 && curState == NormalPhase);
assert(fb && dataFd > 0 && curState == NormalPhase);
DPRINTF(VNC, "Sending framebuffer resize\n");
FrameBufferUpdate fbu;
@ -692,19 +702,23 @@ VncServer::sendFrameBufferResized()
}
void
VncServer::setFrameBufferParams(VideoConvert::Mode mode, uint16_t width,
uint16_t height)
VncServer::setDirty()
{
VncInput::setFrameBufferParams(mode, width, height);
VncInput::setDirty();
if (mode != videoMode || width != videoWidth() || height != videoHeight()) {
if (dataFd > 0 && fbPtr && curState == NormalPhase) {
if (supportsResizeEnc)
sendFrameBufferResized();
else
// The frame buffer changed size and we can't update the client
detach();
}
sendUpdate = true;
sendFrameBufferUpdate();
}
void
VncServer::frameBufferResized()
{
if (dataFd > 0 && curState == NormalPhase) {
if (supportsResizeEnc)
sendFrameBufferResized();
else
// The frame buffer changed size and we can't update the client
detach();
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010 ARM Limited
* Copyright (c) 2010, 2015 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -47,7 +47,6 @@
#include <iostream>
#include "base/vnc/convert.hh"
#include "base/vnc/vncinput.hh"
#include "base/bitmap.hh"
#include "base/circlebuf.hh"
@ -305,24 +304,11 @@ class VncServer : public VncInput
*/
void sendFrameBufferResized();
public:
/** The frame buffer uses this call to notify the vnc server that
* the frame buffer has been updated and a new image needs to be sent to the
* client
*/
void
setDirty()
{
VncInput::setDirty();
sendUpdate = true;
sendFrameBufferUpdate();
}
static const PixelConverter pixelConverter;
/** Set the mode of the data the frame buffer will be sending us
* @param mode the mode
*/
void setFrameBufferParams(VideoConvert::Mode mode, uint16_t width,
uint16_t height);
public:
void setDirty() M5_ATTR_OVERRIDE;
void frameBufferResized() M5_ATTR_OVERRIDE;
};
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2013 ARM Limited
* Copyright (c) 2010-2013, 2015 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -37,15 +37,15 @@
* Authors: Chris Emmons
*/
#include "dev/arm/hdlcd.hh"
#include "base/vnc/vncinput.hh"
#include "base/bitmap.hh"
#include "base/output.hh"
#include "base/trace.hh"
#include "debug/HDLcd.hh"
#include "debug/Uart.hh"
#include "dev/arm/amba_device.hh"
#include "dev/arm/base_gic.hh"
#include "dev/arm/hdlcd.hh"
#include "mem/packet.hh"
#include "mem/packet_access.hh"
#include "sim/system.hh"
@ -63,10 +63,11 @@ HDLcd::HDLcd(const Params *p)
h_sync(0), h_back_porch(0), h_data(0), h_front_porch(0),
polarities(0), command(0), pixel_format(0),
red_select(0), green_select(0), blue_select(0),
pixelClock(p->pixel_clock), vnc(p->vnc), bmp(NULL), pic(NULL),
pixelClock(p->pixel_clock),
fb(0, 0), vnc(p->vnc), bmp(&fb), pic(NULL),
frameReadStartTime(0),
dmaStartAddr(0), dmaCurAddr(0), dmaMaxAddr(0), dmaPendingNum(0),
frameUnderrun(false), virtualDisplayBuffer(NULL), pixelBufferSize(0),
frameUnderrun(false), pixelBufferSize(0),
pixelIndex(0), doUpdateParams(false), frameUnderway(false),
dmaBytesInFlight(0),
startFrameEvent(this), endFrameEvent(this), renderPixelEvent(this),
@ -81,13 +82,11 @@ HDLcd::HDLcd(const Params *p)
dmaDoneEventFree[i] = &dmaDoneEventAll[i];
if (vnc)
vnc->setFramebufferAddr(NULL);
vnc->setFrameBuffer(&fb);
}
HDLcd::~HDLcd()
{
if (virtualDisplayBuffer)
delete [] virtualDisplayBuffer;
}
// read registers and frame buffer
@ -315,8 +314,14 @@ HDLcd::write(PacketPtr pkt)
void
HDLcd::updateVideoParams(bool unserializing = false)
{
const uint16_t bpp = bytesPerPixel() << 3;
const size_t buffer_size = bytesPerPixel() * width() * height();
const uint16_t bpp M5_VAR_USED = bytesPerPixel() << 3;
// Workaround configuration bugs where multiple display
// controllers are attached to the same VNC server by reattaching
// enabled devices. This isn't ideal, but works as long as only
// one display controller is active at a time.
if (command.enable && vnc)
vnc->setFrameBuffer(&fb);
// updating these parameters while LCD is enabled is not supported
if (frameUnderway && !unserializing)
@ -328,19 +333,15 @@ HDLcd::updateVideoParams(bool unserializing = false)
// there must be no outstanding DMA transactions for this to work
if (!unserializing) {
assert(dmaPendingNum == 0);
if (virtualDisplayBuffer)
delete [] virtualDisplayBuffer;
virtualDisplayBuffer = new uint8_t[buffer_size];
memset(virtualDisplayBuffer, 0, buffer_size);
virtualDisplayBuffer.resize(bytesPerPixel() * area());
fb.resize(width(), height());
fb.clear();
std::fill(virtualDisplayBuffer.begin(), virtualDisplayBuffer.end(),
0);
}
assert(virtualDisplayBuffer);
if (vnc)
vnc->setFramebufferAddr(virtualDisplayBuffer);
if (bmp)
delete bmp;
DPRINTF(HDLcd, "bpp = %d\n", bpp);
DPRINTF(HDLcd, "display size = %d x %d\n", width(), height());
#if TRACING_ON
@ -354,61 +355,11 @@ HDLcd::updateVideoParams(bool unserializing = false)
DPRINTF(HDLcd, "simulated refresh rate ~ %.1ffps generating ~ %.1fMB/s "
"traffic ([%.1fMHz, T=%d sim clocks] pclk, %d bpp => %.1fMB/s peak requirement)\n",
fps,
fps * buffer_size / 1024 / 1024,
fps * virtualDisplayBuffer.size() / 1024 / 1024,
(double)SimClock::Frequency / pixelClock / 1000000.0,
pixelClock,
bpp,
(double)(SimClock::Frequency / pixelClock * (bpp / 8)) / 1024 / 1024);
if (pixel_format.big_endian)
panic("Big Endian pixel format not implemented by HDLcd controller");
if (vnc) {
if ((bpp == 24) &&
(red_select.size == 8) &&
(blue_select.size == 8) &&
(green_select.size == 8) &&
(green_select.offset == 8)) {
if ((blue_select.offset == 0) &&
(red_select.offset == 16)) {
vnc->setFrameBufferParams(VideoConvert::rgb8888, width(),
height());
bmp = new Bitmap(VideoConvert::rgb8888, width(), height(),
virtualDisplayBuffer);
DPRINTF(HDLcd, "color mode: rgb888\n");
} else if ((red_select.offset == 0) &&
(blue_select.offset == 16)) {
vnc->setFrameBufferParams(VideoConvert::bgr8888, width(),
height());
bmp = new Bitmap(VideoConvert::bgr8888, width(), height(),
virtualDisplayBuffer);
DPRINTF(HDLcd, "color mode: bgr888\n");
}
} else if ((bpp == 16) &&
(red_select.size == 5) &&
(blue_select.size == 5) &&
(green_select.size == 6) &&
(green_select.offset == 5)) {
if ((blue_select.offset == 0) &&
(red_select.offset == 11)) {
vnc->setFrameBufferParams(VideoConvert::rgb565, width(),
height());
bmp = new Bitmap(VideoConvert::rgb565, width(), height(),
virtualDisplayBuffer);
DPRINTF(HDLcd, "color mode: rgb565\n");
} else if ((red_select.offset == 0) &&
(blue_select.offset == 11)) {
vnc->setFrameBufferParams(VideoConvert::bgr565, width(),
height());
bmp = new Bitmap(VideoConvert::bgr565, width(), height(),
virtualDisplayBuffer);
DPRINTF(HDLcd, "color mode: bgr565\n");
}
} else {
DPRINTF(HDLcd, "color mode: undefined\n");
panic("Unimplemented video mode\n");
}
}
}
void
@ -424,7 +375,7 @@ HDLcd::startFrame()
doUpdateParams = false;
}
frameUnderway = true;
assert(virtualDisplayBuffer);
assert(!virtualDisplayBuffer.empty());
assert(pixelBufferSize == 0);
assert(dmaBytesInFlight == 0);
assert(dmaPendingNum == 0);
@ -484,10 +435,11 @@ HDLcd::fillPixelBuffer()
// will be uncacheable as well. If we have uncacheable and cacheable
// requests in the memory system for the same address it won't be
// pleased
uint8_t *const dma_dst(
virtualDisplayBuffer.data() + dmaCurAddr - dmaStartAddr);
event->setTransactionSize(transaction_size);
dmaPort.dmaAction(MemCmd::ReadReq, dmaCurAddr, transaction_size, event,
virtualDisplayBuffer + dmaCurAddr - dmaStartAddr,
0, Request::UNCACHEABLE);
dma_dst, 0, Request::UNCACHEABLE);
dmaCurAddr += transaction_size;
dmaBytesInFlight += transaction_size;
}
@ -549,6 +501,16 @@ HDLcd::renderPixel()
schedule(renderPixelEvent, nextEventTick);
}
PixelConverter
HDLcd::pixelConverter() const
{
return PixelConverter(
bytesPerPixel(),
red_select.offset, green_select.offset, blue_select.offset,
red_select.size, green_select.size, blue_select.size,
pixel_format.big_endian ? BigEndianByteOrder : LittleEndianByteOrder);
}
void
HDLcd::endFrame() {
assert(pixelBufferSize == 0);
@ -556,6 +518,8 @@ HDLcd::endFrame() {
assert(dmaBytesInFlight == 0);
assert(dmaDoneEventFree.size() == dmaDoneEventAll.size());
fb.copyIn(virtualDisplayBuffer, pixelConverter());
if (vnc)
vnc->setDirty();
@ -563,10 +527,9 @@ HDLcd::endFrame() {
if (!pic)
pic = simout.create(csprintf("%s.framebuffer.bmp", sys->name()), true);
assert(bmp);
assert(pic);
pic->seekp(0);
bmp->write(pic);
bmp.write(*pic);
}
// start the next frame
@ -664,8 +627,7 @@ HDLcd::serialize(std::ostream &os)
SERIALIZE_SCALAR(dmaPendingNum);
SERIALIZE_SCALAR(frameUnderrun);
const size_t buffer_size = bytesPerPixel() * width() * height();
SERIALIZE_ARRAY(virtualDisplayBuffer, buffer_size);
arrayParamOut(os, "virtualDisplayBuffer", virtualDisplayBuffer);
SERIALIZE_SCALAR(pixelBufferSize);
SERIALIZE_SCALAR(pixelIndex);
@ -777,9 +739,7 @@ HDLcd::unserialize(Checkpoint *cp, const std::string &section)
UNSERIALIZE_SCALAR(frameUnderrun);
UNSERIALIZE_SCALAR(dmaBytesInFlight);
const size_t buffer_size = bytesPerPixel() * width() * height();
virtualDisplayBuffer = new uint8_t[buffer_size];
UNSERIALIZE_ARRAY(virtualDisplayBuffer, buffer_size);
arrayParamIn(cp, section, "virtualDisplayBuffer", virtualDisplayBuffer);
UNSERIALIZE_SCALAR(pixelBufferSize);
UNSERIALIZE_SCALAR(pixelIndex);
@ -823,6 +783,8 @@ HDLcd::unserialize(Checkpoint *cp, const std::string &section)
if (frameUnderway) {
updateVideoParams(true);
fb.resize(width(), height());
fb.copyIn(virtualDisplayBuffer, pixelConverter());
if (vnc)
vnc->setDirty();
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2013 ARM Limited
* Copyright (c) 2010-2013, 2015 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -83,13 +83,15 @@
#define __DEV_ARM_HDLCD_HH__
#include <fstream>
#include <memory>
#include "base/bitmap.hh"
#include "base/framebuffer.hh"
#include "dev/arm/amba_device.hh"
#include "params/HDLcd.hh"
#include "sim/serialize.hh"
class VncInput;
class Bitmap;
class HDLcd: public AmbaDmaDevice
{
@ -142,6 +144,8 @@ class HDLcd: public AmbaDmaDevice
/** AXI port width in bytes */
static const size_t AXI_PORT_WIDTH = 8;
static const size_t MAX_BURST_SIZE = MAX_BURST_LEN * AXI_PORT_WIDTH;
/**
* @name RegisterFieldLayouts
* Bit layout declarations for multi-field registers.
@ -242,11 +246,13 @@ class HDLcd: public AmbaDmaDevice
/** Pixel clock period */
const Tick pixelClock;
FrameBuffer fb;
/** VNC server */
VncInput *vnc;
/** Helper to write out bitmaps */
Bitmap *bmp;
Bitmap bmp;
/** Picture of what the current frame buffer looks like */
std::ostream *pic;
@ -325,7 +331,7 @@ class HDLcd: public AmbaDmaDevice
bool frameUnderrun;
/** HDLcd virtual display buffer */
uint8_t *virtualDisplayBuffer;
std::vector<uint8_t> virtualDisplayBuffer;
/** Size of the pixel buffer */
size_t pixelBufferSize;
@ -402,6 +408,8 @@ class HDLcd: public AmbaDmaDevice
return fb_line_count.fb_line_count;
}
inline size_t area() const { return height() * width(); }
/**
* Gets the total number of pixel clocks per display line.
*
@ -436,6 +444,8 @@ class HDLcd: public AmbaDmaDevice
/** Called when it is time to render a pixel */
void renderPixel();
PixelConverter pixelConverter() const;
/** Start of frame event */
EventWrapper<HDLcd, &HDLcd::startFrame> startFrameEvent;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2012 ARM Limited
* Copyright (c) 2010-2012, 2015 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -39,7 +39,6 @@
*/
#include "base/vnc/vncinput.hh"
#include "base/bitmap.hh"
#include "base/output.hh"
#include "base/trace.hh"
#include "debug/PL111.hh"
@ -63,7 +62,9 @@ Pl111::Pl111(const Params *p)
clcdCrsrCtrl(0), clcdCrsrConfig(0), clcdCrsrPalette0(0),
clcdCrsrPalette1(0), clcdCrsrXY(0), clcdCrsrClip(0), clcdCrsrImsc(0),
clcdCrsrIcr(0), clcdCrsrRis(0), clcdCrsrMis(0),
pixelClock(p->pixel_clock), vnc(p->vnc), bmp(NULL), pic(NULL),
pixelClock(p->pixel_clock),
converter(PixelConverter::rgba8888_le), fb(LcdMaxWidth, LcdMaxHeight),
vnc(p->vnc), bmp(&fb), pic(NULL),
width(LcdMaxWidth), height(LcdMaxHeight),
bytesPerPixel(4), startTime(0), startAddr(0), maxAddr(0), curAddr(0),
waterMark(0), dmaPendingNum(0), readEvent(this), fillFifoEvent(this),
@ -83,7 +84,7 @@ Pl111::Pl111(const Params *p)
dmaDoneEventFree[i] = &dmaDoneEventAll[i];
if (vnc)
vnc->setFramebufferAddr(dmaBuffer);
vnc->setFrameBuffer(&fb);
}
Pl111::~Pl111()
@ -378,45 +379,66 @@ Pl111::write(PacketPtr pkt)
return pioDelay;
}
PixelConverter
Pl111::pixelConverter() const
{
unsigned rw, gw, bw;
unsigned offsets[3];
switch (lcdControl.lcdbpp) {
case bpp24:
rw = gw = bw = 8;
offsets[0] = 0;
offsets[1] = 8;
offsets[2] = 16;
break;
case bpp16m565:
rw = 5;
gw = 6;
bw = 5;
offsets[0] = 0;
offsets[1] = 5;
offsets[2] = 11;
break;
default:
panic("Unimplemented video mode\n");
}
if (lcdControl.bgr) {
return PixelConverter(
bytesPerPixel,
offsets[2], offsets[1], offsets[0],
rw, gw, bw,
LittleEndianByteOrder);
} else {
return PixelConverter(
bytesPerPixel,
offsets[0], offsets[1], offsets[2],
rw, gw, bw,
LittleEndianByteOrder);
}
}
void
Pl111::updateVideoParams()
{
if (lcdControl.lcdbpp == bpp24) {
bytesPerPixel = 4;
} else if (lcdControl.lcdbpp == bpp16m565) {
bytesPerPixel = 2;
}
if (lcdControl.lcdbpp == bpp24) {
bytesPerPixel = 4;
} else if (lcdControl.lcdbpp == bpp16m565) {
bytesPerPixel = 2;
}
if (vnc) {
if (lcdControl.lcdbpp == bpp24 && lcdControl.bgr)
vnc->setFrameBufferParams(VideoConvert::bgr8888, width,
height);
else if (lcdControl.lcdbpp == bpp24 && !lcdControl.bgr)
vnc->setFrameBufferParams(VideoConvert::rgb8888, width,
height);
else if (lcdControl.lcdbpp == bpp16m565 && lcdControl.bgr)
vnc->setFrameBufferParams(VideoConvert::bgr565, width,
height);
else if (lcdControl.lcdbpp == bpp16m565 && !lcdControl.bgr)
vnc->setFrameBufferParams(VideoConvert::rgb565, width,
height);
else
panic("Unimplemented video mode\n");
}
fb.resize(width, height);
converter = pixelConverter();
if (bmp)
delete bmp;
if (lcdControl.lcdbpp == bpp24 && lcdControl.bgr)
bmp = new Bitmap(VideoConvert::bgr8888, width, height, dmaBuffer);
else if (lcdControl.lcdbpp == bpp24 && !lcdControl.bgr)
bmp = new Bitmap(VideoConvert::rgb8888, width, height, dmaBuffer);
else if (lcdControl.lcdbpp == bpp16m565 && lcdControl.bgr)
bmp = new Bitmap(VideoConvert::bgr565, width, height, dmaBuffer);
else if (lcdControl.lcdbpp == bpp16m565 && !lcdControl.bgr)
bmp = new Bitmap(VideoConvert::rgb565, width, height, dmaBuffer);
else
panic("Unimplemented video mode\n");
// Workaround configuration bugs where multiple display
// controllers are attached to the same VNC server by reattaching
// enabled devices. This isn't ideal, but works as long as only
// one display controller is active at a time.
if (lcdControl.lcdpwr && vnc)
vnc->setFrameBuffer(&fb);
}
void
@ -493,6 +515,7 @@ Pl111::dmaDone()
}
assert(!readEvent.scheduled());
fb.copyIn(dmaBuffer, converter);
if (vnc)
vnc->setDirty();
@ -502,10 +525,9 @@ Pl111::dmaDone()
if (!pic)
pic = simout.create(csprintf("%s.framebuffer.bmp", sys->name()), true);
assert(bmp);
assert(pic);
pic->seekp(0);
bmp->write(pic);
bmp.write(*pic);
}
// schedule the next read based on when the last frame started
@ -721,6 +743,7 @@ Pl111::unserialize(Checkpoint *cp, const std::string &section)
if (lcdControl.lcdpwr) {
updateVideoParams();
fb.copyIn(dmaBuffer, converter);
if (vnc)
vnc->setDirty();
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2012 ARM Limited
* Copyright (c) 2010-2012, 2015 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -47,13 +47,15 @@
#define __DEV_ARM_PL111_HH__
#include <fstream>
#include <memory>
#include "base/bitmap.hh"
#include "base/framebuffer.hh"
#include "dev/arm/amba_device.hh"
#include "params/Pl111.hh"
#include "sim/serialize.hh"
class VncInput;
class Bitmap;
class Pl111: public AmbaDmaDevice
{
@ -256,11 +258,14 @@ class Pl111: public AmbaDmaDevice
/** Pixel clock */
Tick pixelClock;
PixelConverter converter;
FrameBuffer fb;
/** VNC server */
VncInput *vnc;
/** Helper to write out bitmaps */
Bitmap *bmp;
Bitmap bmp;
/** Picture of what the current frame buffer looks like */
std::ostream *pic;
@ -295,6 +300,8 @@ class Pl111: public AmbaDmaDevice
/** Number of pending dma reads */
uint32_t dmaPendingNum;
PixelConverter pixelConverter() const;
/** Send updated parameters to the vnc server */
void updateVideoParams();

View file

@ -58,8 +58,6 @@
#include <libkern/OSByteOrder.h>
#endif
enum ByteOrder {BigEndianByteOrder, LittleEndianByteOrder};
//These functions actually perform the swapping for parameters
//of various bit lengths
inline uint64_t

View file

@ -37,6 +37,7 @@ UnitTest('bitvectest', 'bitvectest.cc')
UnitTest('circletest', 'circletest.cc')
UnitTest('cprintftest', 'cprintftest.cc')
UnitTest('cprintftime', 'cprintftest.cc')
UnitTest('fbtest', 'fbtest.cc')
UnitTest('initest', 'initest.cc')
UnitTest('nmtest', 'nmtest.cc')
UnitTest('rangemaptest', 'rangemaptest.cc')

126
src/unittest/fbtest.cc Normal file
View file

@ -0,0 +1,126 @@
/*
* Copyright (c) 2015 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors: Andreas Sandberg
*/
#include "base/framebuffer.hh"
#include "unittest/unittest.hh"
static Pixel pixel_red(0xff, 0x00, 0x00);
static Pixel pixel_green(0x00, 0xff, 0x00);
static Pixel pixel_blue(0x00, 0x00, 0xff);
int
main()
{
UnitTest::setCase("Pixel conversion RGBA8888");
{
EXPECT_EQ(PixelConverter::rgba8888_le.fromPixel(pixel_red),
0x000000ffU);
EXPECT_EQ(PixelConverter::rgba8888_le.fromPixel(pixel_green),
0x0000ff00U);
EXPECT_EQ(PixelConverter::rgba8888_le.fromPixel(pixel_blue),
0x00ff0000U);
EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(0x000000ffU),
pixel_red);
EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(0x0000ff00U),
pixel_green);
EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(0x00ff0000U),
pixel_blue);
}
UnitTest::setCase("Pixel conversion RGB565");
{
EXPECT_EQ(PixelConverter::rgb565_le.fromPixel(pixel_red), 0x001fU);
EXPECT_EQ(PixelConverter::rgb565_le.fromPixel(pixel_green), 0x07e0U);
EXPECT_EQ(PixelConverter::rgb565_le.fromPixel(pixel_blue), 0xf800U);
EXPECT_EQ(PixelConverter::rgb565_le.toPixel(0x001fU), pixel_red);
EXPECT_EQ(PixelConverter::rgb565_le.toPixel(0x07e0U), pixel_green);
EXPECT_EQ(PixelConverter::rgb565_le.toPixel(0xf800U), pixel_blue);
}
UnitTest::setCase("Pixel->Mem RGBA8888 LE");
{
uint8_t data[] = { 0xde, 0xad, 0xbe, 0xef };
PixelConverter::rgba8888_le.fromPixel(data, pixel_red);
EXPECT_EQ(data[0], 0xff);
EXPECT_EQ(data[1], 0x00);
EXPECT_EQ(data[3], 0x00);
EXPECT_EQ(data[3], 0x00);
EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(data), pixel_red);
PixelConverter::rgba8888_le.fromPixel(data, pixel_green);
EXPECT_EQ(data[0], 0x00);
EXPECT_EQ(data[1], 0xff);
EXPECT_EQ(data[3], 0x00);
EXPECT_EQ(data[3], 0x00);
EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(data), pixel_green);
PixelConverter::rgba8888_le.fromPixel(data, pixel_blue);
EXPECT_EQ(data[0], 0x00);
EXPECT_EQ(data[1], 0x00);
EXPECT_EQ(data[2], 0xff);
EXPECT_EQ(data[3], 0x00);
EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(data), pixel_blue);
}
UnitTest::setCase("Mem->Pixel RGBA8888 LE");
{
uint8_t red[] = { 0xff, 0x00, 0x00, 0x00 };
uint8_t green[] = { 0x00, 0xff, 0x00, 0x00 };
uint8_t blue[] = { 0x00, 0x00, 0xff, 0x00 };
EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(red), pixel_red);
EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(green), pixel_green);
EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(blue), pixel_blue);
}
UnitTest::setCase("Mem->Pixel RGBA8888 BE");
{
uint8_t red[] = { 0x00, 0x00, 0x00, 0xff };
uint8_t green[] = { 0x00, 0x00, 0xff, 0x00 };
uint8_t blue[] = { 0x00, 0xff, 0x00, 0x00 };
EXPECT_EQ(PixelConverter::rgba8888_be.toPixel(red), pixel_red);
EXPECT_EQ(PixelConverter::rgba8888_be.toPixel(green), pixel_green);
EXPECT_EQ(PixelConverter::rgba8888_be.toPixel(blue), pixel_blue);
}
return UnitTest::printResults();
}