diff --git a/src/base/SConscript b/src/base/SConscript index e7c420f78..a00f8ad66 100644 --- a/src/base/SConscript +++ b/src/base/SConscript @@ -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') diff --git a/src/base/bitmap.cc b/src/base/bitmap.cc index d83a30be3..0052503a4 100644 --- a/src/base/bitmap.cc +++ b/src/base/bitmap.cc @@ -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(&header), sizeof(header)); + bmp.write(reinterpret_cast(&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 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(line_buffer.data()), + line_buffer.size() * sizeof(line_buffer[0])); } - bmp->flush(); - - delete[] pixels; + bmp.flush(); } diff --git a/src/base/bitmap.hh b/src/base/bitmap.hh index b06aff10f..0797a26a7 100644 --- a/src/base/bitmap.hh +++ b/src/base/bitmap.hh @@ -45,7 +45,7 @@ #include #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__ diff --git a/src/base/framebuffer.cc b/src/base/framebuffer.cc new file mode 100644 index 000000000..dc4c2e1c2 --- /dev/null +++ b/src/base/framebuffer.cc @@ -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 + +#include + +#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(pixels.data()), + area() * sizeof(Pixel)); +} diff --git a/src/base/framebuffer.hh b/src/base/framebuffer.hh new file mode 100644 index 000000000..457c6d06d --- /dev/null +++ b/src/base/framebuffer.hh @@ -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 +#include + +#include + +#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(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 &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 &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 pixels; + + protected: + /** Width in pixels */ + unsigned _width; + /** Height in pixels */ + unsigned _height; +}; + +#endif // __BASE_FRAMEBUFFER_HH__ diff --git a/src/base/types.hh b/src/base/types.hh index c4cb4d988..2b6e3f11b 100644 --- a/src/base/types.hh +++ b/src/base/types.hh @@ -188,4 +188,9 @@ typedef std::shared_ptr Fault; constexpr decltype(nullptr) NoFault = nullptr; #endif +enum ByteOrder { + BigEndianByteOrder, + LittleEndianByteOrder +}; + #endif // __BASE_TYPES_HH__ diff --git a/src/base/vnc/SConscript b/src/base/vnc/SConscript index 416743200..271f894ce 100644 --- a/src/base/vnc/SConscript +++ b/src/base/vnc/SConscript @@ -39,8 +39,6 @@ Import('*') -Source('convert.cc') - SimObject('Vnc.py') Source('vncinput.cc') Source('vncserver.cc') diff --git a/src/base/vnc/convert.cc b/src/base/vnc/convert.cc deleted file mode 100644 index 2a52ccaec..000000000 --- a/src/base/vnc/convert.cc +++ /dev/null @@ -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 - -#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; -}*/ diff --git a/src/base/vnc/convert.hh b/src/base/vnc/convert.hh deleted file mode 100644 index 592076cbf..000000000 --- a/src/base/vnc/convert.hh +++ /dev/null @@ -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 -#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__ - diff --git a/src/base/vnc/vncinput.cc b/src/base/vnc/vncinput.cc index 071804583..017fc3876 100644 --- a/src/base/vnc/vncinput.cc +++ b/src/base/vnc/vncinput.cc @@ -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; diff --git a/src/base/vnc/vncinput.hh b/src/base/vnc/vncinput.hh index 1686e3f25..96235fec7 100644 --- a/src/base/vnc/vncinput.hh +++ b/src/base/vnc/vncinput.hh @@ -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 +#include -#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 captureBitmap; /** Captures the current frame buffer to a file */ void captureFrameBuffer(); diff --git a/src/base/vnc/vncserver.cc b/src/base/vnc/vncserver.cc index 6dc2f2f10..20f3bc8fe 100644 --- a/src/base/vnc/vncserver.cc +++ b/src/base/vnc/vncserver.cc @@ -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 #include +#include #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 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(); } } diff --git a/src/base/vnc/vncserver.hh b/src/base/vnc/vncserver.hh index cd1f186f5..0222a7726 100644 --- a/src/base/vnc/vncserver.hh +++ b/src/base/vnc/vncserver.hh @@ -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 -#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 diff --git a/src/dev/arm/hdlcd.cc b/src/dev/arm/hdlcd.cc index 37569b22b..b1c1c450b 100644 --- a/src/dev/arm/hdlcd.cc +++ b/src/dev/arm/hdlcd.cc @@ -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 §ion) 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 §ion) if (frameUnderway) { updateVideoParams(true); + fb.resize(width(), height()); + fb.copyIn(virtualDisplayBuffer, pixelConverter()); if (vnc) vnc->setDirty(); } diff --git a/src/dev/arm/hdlcd.hh b/src/dev/arm/hdlcd.hh index ba22cc163..61d2dc5d7 100644 --- a/src/dev/arm/hdlcd.hh +++ b/src/dev/arm/hdlcd.hh @@ -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 +#include +#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 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 startFrameEvent; diff --git a/src/dev/arm/pl111.cc b/src/dev/arm/pl111.cc index a5b9b412c..fcce09f50 100644 --- a/src/dev/arm/pl111.cc +++ b/src/dev/arm/pl111.cc @@ -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 §ion) if (lcdControl.lcdpwr) { updateVideoParams(); + fb.copyIn(dmaBuffer, converter); if (vnc) vnc->setDirty(); } diff --git a/src/dev/arm/pl111.hh b/src/dev/arm/pl111.hh index a7c158473..85973bbc7 100644 --- a/src/dev/arm/pl111.hh +++ b/src/dev/arm/pl111.hh @@ -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 +#include +#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(); diff --git a/src/sim/byteswap.hh b/src/sim/byteswap.hh index b46c1e552..7e5d6809e 100644 --- a/src/sim/byteswap.hh +++ b/src/sim/byteswap.hh @@ -58,8 +58,6 @@ #include #endif -enum ByteOrder {BigEndianByteOrder, LittleEndianByteOrder}; - //These functions actually perform the swapping for parameters //of various bit lengths inline uint64_t diff --git a/src/unittest/SConscript b/src/unittest/SConscript index 2926311a5..ae22225b3 100644 --- a/src/unittest/SConscript +++ b/src/unittest/SConscript @@ -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') diff --git a/src/unittest/fbtest.cc b/src/unittest/fbtest.cc new file mode 100644 index 000000000..8a8f959ae --- /dev/null +++ b/src/unittest/fbtest.cc @@ -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(); +}