renderbuffer_manager.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/command_buffer/service/renderbuffer_manager.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/debug/trace_event.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stringprintf.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/command_buffer/common/gles2_cmd_utils.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/command_buffer/service/memory_tracking.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gl/gl_implementation.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace gpu {
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace gles2 {
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)RenderbufferManager::RenderbufferManager(
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    MemoryTracker* memory_tracker,
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GLint max_renderbuffer_size,
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GLint max_samples)
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : memory_tracker_(
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          new MemoryTypeTracker(memory_tracker, MemoryTracker::kUnmanaged)),
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      max_renderbuffer_size_(max_renderbuffer_size),
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      max_samples_(max_samples),
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      num_uncleared_renderbuffers_(0),
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      renderbuffer_count_(0),
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      have_context_(true) {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)RenderbufferManager::~RenderbufferManager() {
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(renderbuffers_.empty());
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If this triggers, that means something is keeping a reference to
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // a Renderbuffer belonging to this.
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CHECK_EQ(renderbuffer_count_, 0u);
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(0, num_uncleared_renderbuffers_);
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)size_t Renderbuffer::EstimatedSize() {
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint32 size = 0;
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  RenderbufferManager::ComputeEstimatedRenderbufferSize(
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      width_, height_, samples_, internal_format_, &size);
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return size;
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void Renderbuffer::AddToSignature(
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string* signature) const {
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(signature);
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *signature += base::StringPrintf(
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "|Renderbuffer|internal_format=%04x|samples=%d|width=%d|height=%d",
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      internal_format_, samples_, width_, height_);
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)Renderbuffer::Renderbuffer(RenderbufferManager* manager, GLuint service_id)
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : manager_(manager),
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      deleted_(false),
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      service_id_(service_id),
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      cleared_(true),
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      has_been_bound_(false),
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      samples_(0),
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      internal_format_(GL_RGBA4),
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      width_(0),
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      height_(0) {
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  manager_->StartTracking(this);
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)Renderbuffer::~Renderbuffer() {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (manager_) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (manager_->have_context_) {
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GLuint id = service_id();
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      glDeleteRenderbuffersEXT(1, &id);
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    manager_->StopTracking(this);
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    manager_ = NULL;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RenderbufferManager::Destroy(bool have_context) {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  have_context_ = have_context;
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  renderbuffers_.clear();
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(0u, memory_tracker_->GetMemRepresented());
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void RenderbufferManager::StartTracking(Renderbuffer* /* renderbuffer */) {
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ++renderbuffer_count_;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void RenderbufferManager::StopTracking(Renderbuffer* renderbuffer) {
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  --renderbuffer_count_;
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!renderbuffer->cleared()) {
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    --num_uncleared_renderbuffers_;
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  memory_tracker_->TrackMemFree(renderbuffer->EstimatedSize());
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RenderbufferManager::SetInfo(
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Renderbuffer* renderbuffer,
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) {
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(renderbuffer);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!renderbuffer->cleared()) {
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    --num_uncleared_renderbuffers_;
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  memory_tracker_->TrackMemFree(renderbuffer->EstimatedSize());
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  renderbuffer->SetInfo(samples, internalformat, width, height);
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  memory_tracker_->TrackMemAlloc(renderbuffer->EstimatedSize());
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!renderbuffer->cleared()) {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++num_uncleared_renderbuffers_;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void RenderbufferManager::SetCleared(Renderbuffer* renderbuffer,
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     bool cleared) {
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(renderbuffer);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!renderbuffer->cleared()) {
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    --num_uncleared_renderbuffers_;
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  renderbuffer->set_cleared(cleared);
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!renderbuffer->cleared()) {
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++num_uncleared_renderbuffers_;
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void RenderbufferManager::CreateRenderbuffer(
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GLuint client_id, GLuint service_id) {
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_refptr<Renderbuffer> renderbuffer(new Renderbuffer(this, service_id));
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::pair<RenderbufferMap::iterator, bool> result =
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      renderbuffers_.insert(std::make_pair(client_id, renderbuffer));
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(result.second);
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!renderbuffer->cleared()) {
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++num_uncleared_renderbuffers_;
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)Renderbuffer* RenderbufferManager::GetRenderbuffer(
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GLuint client_id) {
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  RenderbufferMap::iterator it = renderbuffers_.find(client_id);
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return it != renderbuffers_.end() ? it->second : NULL;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void RenderbufferManager::RemoveRenderbuffer(GLuint client_id) {
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  RenderbufferMap::iterator it = renderbuffers_.find(client_id);
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (it != renderbuffers_.end()) {
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Renderbuffer* renderbuffer = it->second;
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    renderbuffer->MarkAsDeleted();
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    renderbuffers_.erase(it);
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool RenderbufferManager::GetClientId(
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GLuint service_id, GLuint* client_id) const {
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This doesn't need to be fast. It's only used during slow queries.
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (RenderbufferMap::const_iterator it = renderbuffers_.begin();
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != renderbuffers_.end(); ++it) {
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (it->second->service_id() == service_id) {
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *client_id = it->first;
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool RenderbufferManager::ComputeEstimatedRenderbufferSize(
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int width, int height, int samples, int internal_format, uint32* size) {
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(size);
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint32 temp = 0;
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!SafeMultiplyUint32(width, height, &temp)) {
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!SafeMultiplyUint32(temp, samples, &temp)) {
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GLenum impl_format = InternalRenderbufferFormatToImplFormat(internal_format);
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!SafeMultiplyUint32(
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      temp, GLES2Util::RenderbufferBytesPerPixel(impl_format), &temp)) {
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *size = temp;
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return true;
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)GLenum RenderbufferManager::InternalRenderbufferFormatToImplFormat(
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GLenum impl_format) {
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2) {
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    switch (impl_format) {
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      case GL_DEPTH_COMPONENT16:
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return GL_DEPTH_COMPONENT;
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      case GL_RGBA4:
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      case GL_RGB5_A1:
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return GL_RGBA;
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      case GL_RGB565:
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return GL_RGB;
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return impl_format;
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace gles2
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace gpu
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
202