blob: 1cd9f6bd84dd62ef6770470894443eef9cca0936 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.idea.editors.gfxtrace.controllers;
import com.android.tools.idea.editors.gfxtrace.GfxTraceEditor;
import com.android.tools.idea.editors.gfxtrace.rpc.RenderSettings;
import com.intellij.execution.ui.layout.impl.JBRunnerTabs;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBLoadingPanel;
import com.intellij.ui.components.JBScrollPane;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
public class FrameBufferController implements GfxController {
@NotNull private final GfxTraceEditor myEditor;
@NotNull private final JBRunnerTabs myBufferTabs;
@NotNull private final JBScrollPane[] myBufferScrollPanes;
@NotNull private AtomicLong myCurrentFetchAtomId = new AtomicLong();
@Nullable private ImageFetcher myImageFetcher;
@NotNull private JBLoadingPanel[] myLoadingPanels = new JBLoadingPanel[BufferType.length];
public FrameBufferController(@NotNull GfxTraceEditor editor,
@NotNull JBRunnerTabs bufferTabs,
@NotNull JBScrollPane colorScrollPane,
@NotNull JBScrollPane wireframePane,
@NotNull JBScrollPane depthScrollPane) {
myEditor = editor;
myBufferTabs = bufferTabs;
myBufferScrollPanes = new JBScrollPane[]{colorScrollPane, wireframePane, depthScrollPane};
assert (myBufferScrollPanes.length == BufferType.length);
for (int i = 0; i < myBufferScrollPanes.length; ++i) {
myBufferScrollPanes[i].getVerticalScrollBar().setUnitIncrement(20);
myBufferScrollPanes[i].getHorizontalScrollBar().setUnitIncrement(20);
myBufferScrollPanes[i].setBorder(BorderFactory.createLineBorder(JBColor.border()));
myLoadingPanels[i] = new JBLoadingPanel(new BorderLayout(), myEditor.getProject());
myBufferScrollPanes[i].setViewportView(myLoadingPanels[i]);
}
// TODO: Add a way to pan the viewport with the keyboard.
}
@Nullable
private static FetchedImage fetchImage(long atomId,
@NotNull ImageFetcher imageFetcher,
@NotNull BufferType instance,
@Nullable RenderSettings renderSettings) {
assert (instance == BufferType.DEPTH_BUFFER || renderSettings != null);
if (renderSettings != null) {
renderSettings.setWireframe(instance == BufferType.WIREFRAME_BUFFER);
}
ImageFetcher.ImageFetchHandle imageFetchHandle =
(instance == BufferType.DEPTH_BUFFER) ? imageFetcher.queueDepthImage(atomId) : imageFetcher.queueColorImage(atomId, renderSettings);
if (imageFetchHandle == null) {
return null;
}
return imageFetcher.resolveImage(imageFetchHandle);
}
@Override
public void startLoad() {
}
@Override
public void commitData(@NotNull GfxContextChangeState state) {
myImageFetcher = new ImageFetcher(myEditor.getClient());
assert (myEditor.getCaptureId() != null);
if (myEditor.getDeviceId() == null || myEditor.getContext() == null) {
// If there is no device selected, don't do anything.
return;
}
myImageFetcher.prepareFetch(myEditor.getDeviceId(), myEditor.getCaptureId(), myEditor.getContext());
}
public void setImageForId(final long atomId) {
// TODO: Add toggle for between scaled and full size.
ApplicationManager.getApplication().assertIsDispatchThread();
assert (myEditor.getCaptureId() != null);
assert (myImageFetcher != null);
if (myCurrentFetchAtomId.get() == atomId) {
// Early out if the given atomId was already fetched or is in the process of being fetched.
return;
}
clearCache();
// Only color, wireframe, and depth are fetched at the moment, since the server hasn't implemented stencil buffers.
final List<BufferType> bufferOrder =
new ArrayList<BufferType>(Arrays.asList(BufferType.COLOR_BUFFER, BufferType.WIREFRAME_BUFFER, BufferType.DEPTH_BUFFER));
for (JBLoadingPanel panel : myLoadingPanels) {
if (!panel.isLoading()) {
panel.startLoading();
}
}
myCurrentFetchAtomId.set(atomId);
final ImageFetcher imageFetcher = myImageFetcher;
// This needs to run in parallel to the main worker thread since it's slow and it doesn't affect the other controllers.
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
RenderSettings renderSettings = new RenderSettings();
renderSettings.setMaxWidth(4096);
renderSettings.setMaxHeight(4096);
// Prioritize the currently selected tab in bufferOrder.
if (myBufferTabs.getSelectedInfo() != null) {
String tabName = myBufferTabs.getSelectedInfo().getText();
for (BufferType buffer : BufferType.values()) {
if (buffer.getName().equals(tabName)) {
if (bufferOrder.remove(buffer)) {
bufferOrder.add(0, buffer);
}
break;
}
}
}
for (BufferType buffer : bufferOrder) {
if (atomId != myCurrentFetchAtomId.get()) {
return;
}
FetchedImage fetchedImage = fetchImage(atomId, imageFetcher, buffer, renderSettings);
if (fetchedImage == null) {
break;
}
setIcons(atomId, fetchedImage.createImageIcon(), buffer);
}
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
if (atomId == myCurrentFetchAtomId.get()) {
stopLoading();
}
}
});
}
});
}
@Override
public void clear() {
myImageFetcher = null;
stopLoading();
clearCache();
}
@Override
public void clearCache() {
for (JBLoadingPanel panel : myLoadingPanels) {
panel.getContentPanel().removeAll();
}
myCurrentFetchAtomId.set(-1);
}
private void stopLoading() {
for (JBLoadingPanel panel : myLoadingPanels) {
if (panel.isLoading()) {
panel.stopLoading();
}
}
}
private void setIcons(final long closedAtomId, @NotNull final ImageIcon image, @NotNull final BufferType bufferType) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
if (myCurrentFetchAtomId.get() == closedAtomId) {
int index = bufferType.ordinal();
myLoadingPanels[index].add(new JBLabel(image));
if (myLoadingPanels[index].isLoading()) {
myLoadingPanels[index].stopLoading();
}
myBufferScrollPanes[index].repaint();
}
}
});
}
public enum BufferType {
COLOR_BUFFER("Color"),
WIREFRAME_BUFFER("Wirefame"),
DEPTH_BUFFER("Depth");
private static final int length = BufferType.values().length;
@NotNull final private String myName;
BufferType(@NotNull String name) {
myName = name;
}
@NotNull
public String getName() {
return myName;
}
@Override
@NotNull
public String toString() {
return getName();
}
}
}